(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
  /*
   * The MIT License (MIT)
   *
   * Copyright (c) 2014 Patrick Gansterer <paroga@paroga.com>
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  
  (function(global, undefined) { "use strict";
  var POW_2_24 = Math.pow(2, -24),
      POW_2_32 = Math.pow(2, 32),
      POW_2_53 = Math.pow(2, 53);
  
  function encode(value) {
    var data = new ArrayBuffer(256);
    var dataView = new DataView(data);
    var lastLength;
    var offset = 0;
  
    function ensureSpace(length) {
      var newByteLength = data.byteLength;
      var requiredLength = offset + length;
      while (newByteLength < requiredLength)
        newByteLength *= 2;
      if (newByteLength !== data.byteLength) {
        var oldDataView = dataView;
        data = new ArrayBuffer(newByteLength);
        dataView = new DataView(data);
        var uint32count = (offset + 3) >> 2;
        for (var i = 0; i < uint32count; ++i)
          dataView.setUint32(i * 4, oldDataView.getUint32(i * 4));
      }
  
      lastLength = length;
      return dataView;
    }
    function write() {
      offset += lastLength;
    }
    function writeFloat64(value) {
      write(ensureSpace(8).setFloat64(offset, value));
    }
    function writeUint8(value) {
      write(ensureSpace(1).setUint8(offset, value));
    }
    function writeUint8Array(value) {
      var dataView = ensureSpace(value.length);
      for (var i = 0; i < value.length; ++i)
        dataView.setUint8(offset + i, value[i]);
      write();
    }
    function writeUint16(value) {
      write(ensureSpace(2).setUint16(offset, value));
    }
    function writeUint32(value) {
      write(ensureSpace(4).setUint32(offset, value));
    }
    function writeUint64(value) {
      var low = value % POW_2_32;
      var high = (value - low) / POW_2_32;
      var dataView = ensureSpace(8);
      dataView.setUint32(offset, high);
      dataView.setUint32(offset + 4, low);
      write();
    }
    function writeTypeAndLength(type, length) {
      if (length < 24) {
        writeUint8(type << 5 | length);
      } else if (length < 0x100) {
        writeUint8(type << 5 | 24);
        writeUint8(length);
      } else if (length < 0x10000) {
        writeUint8(type << 5 | 25);
        writeUint16(length);
      } else if (length < 0x100000000) {
        writeUint8(type << 5 | 26);
        writeUint32(length);
      } else {
        writeUint8(type << 5 | 27);
        writeUint64(length);
      }
    }
    
    function encodeItem(value) {
      var i;
  
      if (value === false)
        return writeUint8(0xf4);
      if (value === true)
        return writeUint8(0xf5);
      if (value === null)
        return writeUint8(0xf6);
      if (value === undefined)
        return writeUint8(0xf7);
    
      switch (typeof value) {
        case "number":
          if (Math.floor(value) === value) {
            if (0 <= value && value <= POW_2_53)
              return writeTypeAndLength(0, value);
            if (-POW_2_53 <= value && value < 0)
              return writeTypeAndLength(1, -(value + 1));
          }
          writeUint8(0xfb);
          return writeFloat64(value);
  
        case "string":
          var utf8data = [];
          for (i = 0; i < value.length; ++i) {
            var charCode = value.charCodeAt(i);
            if (charCode < 0x80) {
              utf8data.push(charCode);
            } else if (charCode < 0x800) {
              utf8data.push(0xc0 | charCode >> 6);
              utf8data.push(0x80 | charCode & 0x3f);
            } else if (charCode < 0xd800) {
              utf8data.push(0xe0 | charCode >> 12);
              utf8data.push(0x80 | (charCode >> 6)  & 0x3f);
              utf8data.push(0x80 | charCode & 0x3f);
            } else {
              charCode = (charCode & 0x3ff) << 10;
              charCode |= value.charCodeAt(++i) & 0x3ff;
              charCode += 0x10000;
  
              utf8data.push(0xf0 | charCode >> 18);
              utf8data.push(0x80 | (charCode >> 12)  & 0x3f);
              utf8data.push(0x80 | (charCode >> 6)  & 0x3f);
              utf8data.push(0x80 | charCode & 0x3f);
            }
          }
  
          writeTypeAndLength(3, utf8data.length);
          return writeUint8Array(utf8data);
  
        default:
          var length;
          if (Array.isArray(value)) {
            length = value.length;
            writeTypeAndLength(4, length);
            for (i = 0; i < length; ++i)
              encodeItem(value[i]);
          } else if (value instanceof Uint8Array) {
            writeTypeAndLength(2, value.length);
            writeUint8Array(value);
          } else {
            var keys = Object.keys(value);
            length = keys.length;
            writeTypeAndLength(5, length);
            for (i = 0; i < length; ++i) {
              var key = keys[i];
              encodeItem(key);
              encodeItem(value[key]);
            }
          }
      }
    }
    
    encodeItem(value);
  
    if ("slice" in data)
      return data.slice(0, offset);
    
    var ret = new ArrayBuffer(offset);
    var retView = new DataView(ret);
    for (var i = 0; i < offset; ++i)
      retView.setUint8(i, dataView.getUint8(i));
    return ret;
  }
  
  function decode(data, tagger, simpleValue) {
    var dataView = new DataView(data);
    var offset = 0;
    
    if (typeof tagger !== "function")
      tagger = function(value) { return value; };
    if (typeof simpleValue !== "function")
      simpleValue = function() { return undefined; };
  
    function read(value, length) {
      offset += length;
      return value;
    }
    function readArrayBuffer(length) {
      return read(new Uint8Array(data, offset, length), length);
    }
    function readFloat16() {
      var tempArrayBuffer = new ArrayBuffer(4);
      var tempDataView = new DataView(tempArrayBuffer);
      var value = readUint16();
  
      var sign = value & 0x8000;
      var exponent = value & 0x7c00;
      var fraction = value & 0x03ff;
      
      if (exponent === 0x7c00)
        exponent = 0xff << 10;
      else if (exponent !== 0)
        exponent += (127 - 15) << 10;
      else if (fraction !== 0)
        return fraction * POW_2_24;
      
      tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13);
      return tempDataView.getFloat32(0);
    }
    function readFloat32() {
      return read(dataView.getFloat32(offset), 4);
    }
    function readFloat64() {
      return read(dataView.getFloat64(offset), 8);
    }
    function readUint8() {
      return read(dataView.getUint8(offset), 1);
    }
    function readUint16() {
      return read(dataView.getUint16(offset), 2);
    }
    function readUint32() {
      return read(dataView.getUint32(offset), 4);
    }
    function readUint64() {
      return readUint32() * POW_2_32 + readUint32();
    }
    function readBreak() {
      if (dataView.getUint8(offset) !== 0xff)
        return false;
      offset += 1;
      return true;
    }
    function readLength(additionalInformation) {
      if (additionalInformation < 24)
        return additionalInformation;
      if (additionalInformation === 24)
        return readUint8();
      if (additionalInformation === 25)
        return readUint16();
      if (additionalInformation === 26)
        return readUint32();
      if (additionalInformation === 27)
        return readUint64();
      if (additionalInformation === 31)
        return -1;
      throw "Invalid length encoding";
    }
    function readIndefiniteStringLength(majorType) {
      var initialByte = readUint8();
      if (initialByte === 0xff)
        return -1;
      var length = readLength(initialByte & 0x1f);
      if (length < 0 || (initialByte >> 5) !== majorType)
        throw "Invalid indefinite length element";
      return length;
    }
  
    function appendUtf16data(utf16data, length) {
      for (var i = 0; i < length; ++i) {
        var value = readUint8();
        if (value & 0x80) {
          if (value < 0xe0) {
            value = (value & 0x1f) <<  6
                  | (readUint8() & 0x3f);
            length -= 1;
          } else if (value < 0xf0) {
            value = (value & 0x0f) << 12
                  | (readUint8() & 0x3f) << 6
                  | (readUint8() & 0x3f);
            length -= 2;
          } else {
            value = (value & 0x0f) << 18
                  | (readUint8() & 0x3f) << 12
                  | (readUint8() & 0x3f) << 6
                  | (readUint8() & 0x3f);
            length -= 3;
          }
        }
  
        if (value < 0x10000) {
          utf16data.push(value);
        } else {
          value -= 0x10000;
          utf16data.push(0xd800 | (value >> 10));
          utf16data.push(0xdc00 | (value & 0x3ff));
        }
      }
    }
  
    function decodeItem() {
      var initialByte = readUint8();
      var majorType = initialByte >> 5;
      var additionalInformation = initialByte & 0x1f;
      var i;
      var length;
  
      if (majorType === 7) {
        switch (additionalInformation) {
          case 25:
            return readFloat16();
          case 26:
            return readFloat32();
          case 27:
            return readFloat64();
        }
      }
  
      length = readLength(additionalInformation);
      if (length < 0 && (majorType < 2 || 6 < majorType))
        throw "Invalid length";
  
      switch (majorType) {
        case 0:
          return length;
        case 1:
          return -1 - length;
        case 2:
          if (length < 0) {
            var elements = [];
            var fullArrayLength = 0;
            while ((length = readIndefiniteStringLength(majorType)) >= 0) {
              fullArrayLength += length;
              elements.push(readArrayBuffer(length));
            }
            var fullArray = new Uint8Array(fullArrayLength);
            var fullArrayOffset = 0;
            for (i = 0; i < elements.length; ++i) {
              fullArray.set(elements[i], fullArrayOffset);
              fullArrayOffset += elements[i].length;
            }
            return fullArray;
          }
          return readArrayBuffer(length);
        case 3:
          var utf16data = [];
          if (length < 0) {
            while ((length = readIndefiniteStringLength(majorType)) >= 0)
              appendUtf16data(utf16data, length);
          } else
            appendUtf16data(utf16data, length);
          return String.fromCharCode.apply(null, utf16data);
        case 4:
          var retArray;
          if (length < 0) {
            retArray = [];
            while (!readBreak())
              retArray.push(decodeItem());
          } else {
            retArray = new Array(length);
            for (i = 0; i < length; ++i)
              retArray[i] = decodeItem();
          }
          return retArray;
        case 5:
          var retObject = {};
          for (i = 0; i < length || length < 0 && !readBreak(); ++i) {
            var key = decodeItem();
            retObject[key] = decodeItem();
          }
          return retObject;
        case 6:
          return tagger(decodeItem(), length);
        case 7:
          switch (length) {
            case 20:
              return false;
            case 21:
              return true;
            case 22:
              return null;
            case 23:
              return undefined;
            default:
              return simpleValue(length);
          }
      }
    }
  
    var ret = decodeItem();
    if (offset !== data.byteLength)
      throw "Remaining bytes";
    return ret;
  }
  
  var obj = { encode: encode, decode: decode };
  
  if (typeof define === "function" && define.amd)
    define("cbor/cbor", obj);
  else if (typeof module !== 'undefined' && module.exports)
    module.exports = obj;
  else if (!global.CBOR)
    global.CBOR = obj;
  
  })(this);
  
  },{}],2:[function(require,module,exports){
  (function (process){
  /*!
   * EventEmitter2
   * https://github.com/hij1nx/EventEmitter2
   *
   * Copyright (c) 2013 hij1nx
   * Licensed under the MIT license.
   */
  ;!function(undefined) {
  
    var isArray = Array.isArray ? Array.isArray : function _isArray(obj) {
      return Object.prototype.toString.call(obj) === "[object Array]";
    };
    var defaultMaxListeners = 10;
  
    function init() {
      this._events = {};
      if (this._conf) {
        configure.call(this, this._conf);
      }
    }
  
    function configure(conf) {
      if (conf) {
        this._conf = conf;
  
        conf.delimiter && (this.delimiter = conf.delimiter);
        this._maxListeners = conf.maxListeners !== undefined ? conf.maxListeners : defaultMaxListeners;
  
        conf.wildcard && (this.wildcard = conf.wildcard);
        conf.newListener && (this.newListener = conf.newListener);
        conf.verboseMemoryLeak && (this.verboseMemoryLeak = conf.verboseMemoryLeak);
  
        if (this.wildcard) {
          this.listenerTree = {};
        }
      } else {
        this._maxListeners = defaultMaxListeners;
      }
    }
  
    function logPossibleMemoryLeak(count, eventName) {
      var errorMsg = '(node) warning: possible EventEmitter memory ' +
          'leak detected. ' + count + ' listeners added. ' +
          'Use emitter.setMaxListeners() to increase limit.';
  
      if(this.verboseMemoryLeak){
        errorMsg += ' Event name: ' + eventName + '.';
      }
  
      if(typeof process !== 'undefined' && process.emitWarning){
        var e = new Error(errorMsg);
        e.name = 'MaxListenersExceededWarning';
        e.emitter = this;
        e.count = count;
        process.emitWarning(e);
      } else {
        console.error(errorMsg);
  
        if (console.trace){
          console.trace();
        }
      }
    }
  
    function EventEmitter(conf) {
      this._events = {};
      this.newListener = false;
      this.verboseMemoryLeak = false;
      configure.call(this, conf);
    }
    EventEmitter.EventEmitter2 = EventEmitter; // backwards compatibility for exporting EventEmitter property
  
    //
    // Attention, function return type now is array, always !
    // It has zero elements if no any matches found and one or more
    // elements (leafs) if there are matches
    //
    function searchListenerTree(handlers, type, tree, i) {
      if (!tree) {
        return [];
      }
      var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached,
          typeLength = type.length, currentType = type[i], nextType = type[i+1];
      if (i === typeLength && tree._listeners) {
        //
        // If at the end of the event(s) list and the tree has listeners
        // invoke those listeners.
        //
        if (typeof tree._listeners === 'function') {
          handlers && handlers.push(tree._listeners);
          return [tree];
        } else {
          for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) {
            handlers && handlers.push(tree._listeners[leaf]);
          }
          return [tree];
        }
      }
  
      if ((currentType === '*' || currentType === '**') || tree[currentType]) {
        //
        // If the event emitted is '*' at this part
        // or there is a concrete match at this patch
        //
        if (currentType === '*') {
          for (branch in tree) {
            if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1));
            }
          }
          return listeners;
        } else if(currentType === '**') {
          endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*'));
          if(endReached && tree._listeners) {
            // The next element has a _listeners, add it to the handlers.
            listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength));
          }
  
          for (branch in tree) {
            if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
              if(branch === '*' || branch === '**') {
                if(tree[branch]._listeners && !endReached) {
                  listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength));
                }
                listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
              } else if(branch === nextType) {
                listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2));
              } else {
                // No match on this one, shift into the tree but not in the type array.
                listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
              }
            }
          }
          return listeners;
        }
  
        listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1));
      }
  
      xTree = tree['*'];
      if (xTree) {
        //
        // If the listener tree will allow any match for this part,
        // then recursively explore all branches of the tree
        //
        searchListenerTree(handlers, type, xTree, i+1);
      }
  
      xxTree = tree['**'];
      if(xxTree) {
        if(i < typeLength) {
          if(xxTree._listeners) {
            // If we have a listener on a '**', it will catch all, so add its handler.
            searchListenerTree(handlers, type, xxTree, typeLength);
          }
  
          // Build arrays of matching next branches and others.
          for(branch in xxTree) {
            if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) {
              if(branch === nextType) {
                // We know the next element will match, so jump twice.
                searchListenerTree(handlers, type, xxTree[branch], i+2);
              } else if(branch === currentType) {
                // Current node matches, move into the tree.
                searchListenerTree(handlers, type, xxTree[branch], i+1);
              } else {
                isolatedBranch = {};
                isolatedBranch[branch] = xxTree[branch];
                searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1);
              }
            }
          }
        } else if(xxTree._listeners) {
          // We have reached the end and still on a '**'
          searchListenerTree(handlers, type, xxTree, typeLength);
        } else if(xxTree['*'] && xxTree['*']._listeners) {
          searchListenerTree(handlers, type, xxTree['*'], typeLength);
        }
      }
  
      return listeners;
    }
  
    function growListenerTree(type, listener) {
  
      type = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
  
      //
      // Looks for two consecutive '**', if so, don't add the event at all.
      //
      for(var i = 0, len = type.length; i+1 < len; i++) {
        if(type[i] === '**' && type[i+1] === '**') {
          return;
        }
      }
  
      var tree = this.listenerTree;
      var name = type.shift();
  
      while (name !== undefined) {
  
        if (!tree[name]) {
          tree[name] = {};
        }
  
        tree = tree[name];
  
        if (type.length === 0) {
  
          if (!tree._listeners) {
            tree._listeners = listener;
          }
          else {
            if (typeof tree._listeners === 'function') {
              tree._listeners = [tree._listeners];
            }
  
            tree._listeners.push(listener);
  
            if (
              !tree._listeners.warned &&
              this._maxListeners > 0 &&
              tree._listeners.length > this._maxListeners
            ) {
              tree._listeners.warned = true;
              logPossibleMemoryLeak.call(this, tree._listeners.length, name);
            }
          }
          return true;
        }
        name = type.shift();
      }
      return true;
    }
  
    // By default EventEmitters will print a warning if more than
    // 10 listeners are added to it. This is a useful default which
    // helps finding memory leaks.
    //
    // Obviously not all Emitters should be limited to 10. This function allows
    // that to be increased. Set to zero for unlimited.
  
    EventEmitter.prototype.delimiter = '.';
  
    EventEmitter.prototype.setMaxListeners = function(n) {
      if (n !== undefined) {
        this._maxListeners = n;
        if (!this._conf) this._conf = {};
        this._conf.maxListeners = n;
      }
    };
  
    EventEmitter.prototype.event = '';
  
  
    EventEmitter.prototype.once = function(event, fn) {
      return this._once(event, fn, false);
    };
  
    EventEmitter.prototype.prependOnceListener = function(event, fn) {
      return this._once(event, fn, true);
    };
  
    EventEmitter.prototype._once = function(event, fn, prepend) {
      this._many(event, 1, fn, prepend);
      return this;
    };
  
    EventEmitter.prototype.many = function(event, ttl, fn) {
      return this._many(event, ttl, fn, false);
    }
  
    EventEmitter.prototype.prependMany = function(event, ttl, fn) {
      return this._many(event, ttl, fn, true);
    }
  
    EventEmitter.prototype._many = function(event, ttl, fn, prepend) {
      var self = this;
  
      if (typeof fn !== 'function') {
        throw new Error('many only accepts instances of Function');
      }
  
      function listener() {
        if (--ttl === 0) {
          self.off(event, listener);
        }
        return fn.apply(this, arguments);
      }
  
      listener._origin = fn;
  
      this._on(event, listener, prepend);
  
      return self;
    };
  
    EventEmitter.prototype.emit = function() {
  
      this._events || init.call(this);
  
      var type = arguments[0];
  
      if (type === 'newListener' && !this.newListener) {
        if (!this._events.newListener) {
          return false;
        }
      }
  
      var al = arguments.length;
      var args,l,i,j;
      var handler;
  
      if (this._all && this._all.length) {
        handler = this._all.slice();
        if (al > 3) {
          args = new Array(al);
          for (j = 0; j < al; j++) args[j] = arguments[j];
        }
  
        for (i = 0, l = handler.length; i < l; i++) {
          this.event = type;
          switch (al) {
          case 1:
            handler[i].call(this, type);
            break;
          case 2:
            handler[i].call(this, type, arguments[1]);
            break;
          case 3:
            handler[i].call(this, type, arguments[1], arguments[2]);
            break;
          default:
            handler[i].apply(this, args);
          }
        }
      }
  
      if (this.wildcard) {
        handler = [];
        var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
        searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
      } else {
        handler = this._events[type];
        if (typeof handler === 'function') {
          this.event = type;
          switch (al) {
          case 1:
            handler.call(this);
            break;
          case 2:
            handler.call(this, arguments[1]);
            break;
          case 3:
            handler.call(this, arguments[1], arguments[2]);
            break;
          default:
            args = new Array(al - 1);
            for (j = 1; j < al; j++) args[j - 1] = arguments[j];
            handler.apply(this, args);
          }
          return true;
        } else if (handler) {
          // need to make copy of handlers because list can change in the middle
          // of emit call
          handler = handler.slice();
        }
      }
  
      if (handler && handler.length) {
        if (al > 3) {
          args = new Array(al - 1);
          for (j = 1; j < al; j++) args[j - 1] = arguments[j];
        }
        for (i = 0, l = handler.length; i < l; i++) {
          this.event = type;
          switch (al) {
          case 1:
            handler[i].call(this);
            break;
          case 2:
            handler[i].call(this, arguments[1]);
            break;
          case 3:
            handler[i].call(this, arguments[1], arguments[2]);
            break;
          default:
            handler[i].apply(this, args);
          }
        }
        return true;
      } else if (!this._all && type === 'error') {
        if (arguments[1] instanceof Error) {
          throw arguments[1]; // Unhandled 'error' event
        } else {
          throw new Error("Uncaught, unspecified 'error' event.");
        }
        return false;
      }
  
      return !!this._all;
    };
  
    EventEmitter.prototype.emitAsync = function() {
  
      this._events || init.call(this);
  
      var type = arguments[0];
  
      if (type === 'newListener' && !this.newListener) {
          if (!this._events.newListener) { return Promise.resolve([false]); }
      }
  
      var promises= [];
  
      var al = arguments.length;
      var args,l,i,j;
      var handler;
  
      if (this._all) {
        if (al > 3) {
          args = new Array(al);
          for (j = 1; j < al; j++) args[j] = arguments[j];
        }
        for (i = 0, l = this._all.length; i < l; i++) {
          this.event = type;
          switch (al) {
          case 1:
            promises.push(this._all[i].call(this, type));
            break;
          case 2:
            promises.push(this._all[i].call(this, type, arguments[1]));
            break;
          case 3:
            promises.push(this._all[i].call(this, type, arguments[1], arguments[2]));
            break;
          default:
            promises.push(this._all[i].apply(this, args));
          }
        }
      }
  
      if (this.wildcard) {
        handler = [];
        var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
        searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
      } else {
        handler = this._events[type];
      }
  
      if (typeof handler === 'function') {
        this.event = type;
        switch (al) {
        case 1:
          promises.push(handler.call(this));
          break;
        case 2:
          promises.push(handler.call(this, arguments[1]));
          break;
        case 3:
          promises.push(handler.call(this, arguments[1], arguments[2]));
          break;
        default:
          args = new Array(al - 1);
          for (j = 1; j < al; j++) args[j - 1] = arguments[j];
          promises.push(handler.apply(this, args));
        }
      } else if (handler && handler.length) {
        handler = handler.slice();
        if (al > 3) {
          args = new Array(al - 1);
          for (j = 1; j < al; j++) args[j - 1] = arguments[j];
        }
        for (i = 0, l = handler.length; i < l; i++) {
          this.event = type;
          switch (al) {
          case 1:
            promises.push(handler[i].call(this));
            break;
          case 2:
            promises.push(handler[i].call(this, arguments[1]));
            break;
          case 3:
            promises.push(handler[i].call(this, arguments[1], arguments[2]));
            break;
          default:
            promises.push(handler[i].apply(this, args));
          }
        }
      } else if (!this._all && type === 'error') {
        if (arguments[1] instanceof Error) {
          return Promise.reject(arguments[1]); // Unhandled 'error' event
        } else {
          return Promise.reject("Uncaught, unspecified 'error' event.");
        }
      }
  
      return Promise.all(promises);
    };
  
    EventEmitter.prototype.on = function(type, listener) {
      return this._on(type, listener, false);
    };
  
    EventEmitter.prototype.prependListener = function(type, listener) {
      return this._on(type, listener, true);
    };
  
    EventEmitter.prototype.onAny = function(fn) {
      return this._onAny(fn, false);
    };
  
    EventEmitter.prototype.prependAny = function(fn) {
      return this._onAny(fn, true);
    };
  
    EventEmitter.prototype.addListener = EventEmitter.prototype.on;
  
    EventEmitter.prototype._onAny = function(fn, prepend){
      if (typeof fn !== 'function') {
        throw new Error('onAny only accepts instances of Function');
      }
  
      if (!this._all) {
        this._all = [];
      }
  
      // Add the function to the event listener collection.
      if(prepend){
        this._all.unshift(fn);
      }else{
        this._all.push(fn);
      }
  
      return this;
    }
  
    EventEmitter.prototype._on = function(type, listener, prepend) {
      if (typeof type === 'function') {
        this._onAny(type, listener);
        return this;
      }
  
      if (typeof listener !== 'function') {
        throw new Error('on only accepts instances of Function');
      }
      this._events || init.call(this);
  
      // To avoid recursion in the case that type == "newListeners"! Before
      // adding it to the listeners, first emit "newListeners".
      this.emit('newListener', type, listener);
  
      if (this.wildcard) {
        growListenerTree.call(this, type, listener);
        return this;
      }
  
      if (!this._events[type]) {
        // Optimize the case of one listener. Don't need the extra array object.
        this._events[type] = listener;
      }
      else {
        if (typeof this._events[type] === 'function') {
          // Change to array.
          this._events[type] = [this._events[type]];
        }
  
        // If we've already got an array, just add
        if(prepend){
          this._events[type].unshift(listener);
        }else{
          this._events[type].push(listener);
        }
  
        // Check for listener leak
        if (
          !this._events[type].warned &&
          this._maxListeners > 0 &&
          this._events[type].length > this._maxListeners
        ) {
          this._events[type].warned = true;
          logPossibleMemoryLeak.call(this, this._events[type].length, type);
        }
      }
  
      return this;
    }
  
    EventEmitter.prototype.off = function(type, listener) {
      if (typeof listener !== 'function') {
        throw new Error('removeListener only takes instances of Function');
      }
  
      var handlers,leafs=[];
  
      if(this.wildcard) {
        var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
        leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
      }
      else {
        // does not use listeners(), so no side effect of creating _events[type]
        if (!this._events[type]) return this;
        handlers = this._events[type];
        leafs.push({_listeners:handlers});
      }
  
      for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) {
        var leaf = leafs[iLeaf];
        handlers = leaf._listeners;
        if (isArray(handlers)) {
  
          var position = -1;
  
          for (var i = 0, length = handlers.length; i < length; i++) {
            if (handlers[i] === listener ||
              (handlers[i].listener && handlers[i].listener === listener) ||
              (handlers[i]._origin && handlers[i]._origin === listener)) {
              position = i;
              break;
            }
          }
  
          if (position < 0) {
            continue;
          }
  
          if(this.wildcard) {
            leaf._listeners.splice(position, 1);
          }
          else {
            this._events[type].splice(position, 1);
          }
  
          if (handlers.length === 0) {
            if(this.wildcard) {
              delete leaf._listeners;
            }
            else {
              delete this._events[type];
            }
          }
  
          this.emit("removeListener", type, listener);
  
          return this;
        }
        else if (handlers === listener ||
          (handlers.listener && handlers.listener === listener) ||
          (handlers._origin && handlers._origin === listener)) {
          if(this.wildcard) {
            delete leaf._listeners;
          }
          else {
            delete this._events[type];
          }
  
          this.emit("removeListener", type, listener);
        }
      }
  
      function recursivelyGarbageCollect(root) {
        if (root === undefined) {
          return;
        }
        var keys = Object.keys(root);
        for (var i in keys) {
          var key = keys[i];
          var obj = root[key];
          if ((obj instanceof Function) || (typeof obj !== "object") || (obj === null))
            continue;
          if (Object.keys(obj).length > 0) {
            recursivelyGarbageCollect(root[key]);
          }
          if (Object.keys(obj).length === 0) {
            delete root[key];
          }
        }
      }
      recursivelyGarbageCollect(this.listenerTree);
  
      return this;
    };
  
    EventEmitter.prototype.offAny = function(fn) {
      var i = 0, l = 0, fns;
      if (fn && this._all && this._all.length > 0) {
        fns = this._all;
        for(i = 0, l = fns.length; i < l; i++) {
          if(fn === fns[i]) {
            fns.splice(i, 1);
            this.emit("removeListenerAny", fn);
            return this;
          }
        }
      } else {
        fns = this._all;
        for(i = 0, l = fns.length; i < l; i++)
          this.emit("removeListenerAny", fns[i]);
        this._all = [];
      }
      return this;
    };
  
    EventEmitter.prototype.removeListener = EventEmitter.prototype.off;
  
    EventEmitter.prototype.removeAllListeners = function(type) {
      if (arguments.length === 0) {
        !this._events || init.call(this);
        return this;
      }
  
      if (this.wildcard) {
        var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
        var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
  
        for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) {
          var leaf = leafs[iLeaf];
          leaf._listeners = null;
        }
      }
      else if (this._events) {
        this._events[type] = null;
      }
      return this;
    };
  
    EventEmitter.prototype.listeners = function(type) {
      if (this.wildcard) {
        var handlers = [];
        var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
        searchListenerTree.call(this, handlers, ns, this.listenerTree, 0);
        return handlers;
      }
  
      this._events || init.call(this);
  
      if (!this._events[type]) this._events[type] = [];
      if (!isArray(this._events[type])) {
        this._events[type] = [this._events[type]];
      }
      return this._events[type];
    };
  
    EventEmitter.prototype.eventNames = function(){
      return Object.keys(this._events);
    }
  
    EventEmitter.prototype.listenerCount = function(type) {
      return this.listeners(type).length;
    };
  
    EventEmitter.prototype.listenersAny = function() {
  
      if(this._all) {
        return this._all;
      }
      else {
        return [];
      }
  
    };
  
    if (typeof define === 'function' && define.amd) {
       // AMD. Register as an anonymous module.
      define(function() {
        return EventEmitter;
      });
    } else if (typeof exports === 'object') {
      // CommonJS
      module.exports = EventEmitter;
    }
    else {
      // Browser global.
      window.EventEmitter2 = EventEmitter;
    }
  }();
  
  }).call(this,require('_process'))
  },{"_process":4}],3:[function(require,module,exports){
  /*
  object-assign
  (c) Sindre Sorhus
  @license MIT
  */
  
  'use strict';
  /* eslint-disable no-unused-vars */
  var getOwnPropertySymbols = Object.getOwnPropertySymbols;
  var hasOwnProperty = Object.prototype.hasOwnProperty;
  var propIsEnumerable = Object.prototype.propertyIsEnumerable;
  
  function toObject(val) {
    if (val === null || val === undefined) {
      throw new TypeError('Object.assign cannot be called with null or undefined');
    }
  
    return Object(val);
  }
  
  function shouldUseNative() {
    try {
      if (!Object.assign) {
        return false;
      }
  
      // Detect buggy property enumeration order in older V8 versions.
  
      // https://bugs.chromium.org/p/v8/issues/detail?id=4118
      var test1 = new String('abc');  // eslint-disable-line no-new-wrappers
      test1[5] = 'de';
      if (Object.getOwnPropertyNames(test1)[0] === '5') {
        return false;
      }
  
      // https://bugs.chromium.org/p/v8/issues/detail?id=3056
      var test2 = {};
      for (var i = 0; i < 10; i++) {
        test2['_' + String.fromCharCode(i)] = i;
      }
      var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
        return test2[n];
      });
      if (order2.join('') !== '0123456789') {
        return false;
      }
  
      // https://bugs.chromium.org/p/v8/issues/detail?id=3056
      var test3 = {};
      'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
        test3[letter] = letter;
      });
      if (Object.keys(Object.assign({}, test3)).join('') !==
          'abcdefghijklmnopqrst') {
        return false;
      }
  
      return true;
    } catch (err) {
      // We don't expect any of the above to throw, but better to be safe.
      return false;
    }
  }
  
  module.exports = shouldUseNative() ? Object.assign : function (target, source) {
    var from;
    var to = toObject(target);
    var symbols;
  
    for (var s = 1; s < arguments.length; s++) {
      from = Object(arguments[s]);
  
      for (var key in from) {
        if (hasOwnProperty.call(from, key)) {
          to[key] = from[key];
        }
      }
  
      if (getOwnPropertySymbols) {
        symbols = getOwnPropertySymbols(from);
        for (var i = 0; i < symbols.length; i++) {
          if (propIsEnumerable.call(from, symbols[i])) {
            to[symbols[i]] = from[symbols[i]];
          }
        }
      }
    }
  
    return to;
  };
  
  },{}],4:[function(require,module,exports){
  // shim for using process in browser
  var process = module.exports = {};
  
  // cached from whatever global is present so that test runners that stub it
  // don't break things.  But we need to wrap it in a try catch in case it is
  // wrapped in strict mode code which doesn't define any globals.  It's inside a
  // function because try/catches deoptimize in certain engines.
  
  var cachedSetTimeout;
  var cachedClearTimeout;
  
  function defaultSetTimout() {
      throw new Error('setTimeout has not been defined');
  }
  function defaultClearTimeout () {
      throw new Error('clearTimeout has not been defined');
  }
  (function () {
      try {
          if (typeof setTimeout === 'function') {
              cachedSetTimeout = setTimeout;
          } else {
              cachedSetTimeout = defaultSetTimout;
          }
      } catch (e) {
          cachedSetTimeout = defaultSetTimout;
      }
      try {
          if (typeof clearTimeout === 'function') {
              cachedClearTimeout = clearTimeout;
          } else {
              cachedClearTimeout = defaultClearTimeout;
          }
      } catch (e) {
          cachedClearTimeout = defaultClearTimeout;
      }
  } ())
  function runTimeout(fun) {
      if (cachedSetTimeout === setTimeout) {
          //normal enviroments in sane situations
          return setTimeout(fun, 0);
      }
      // if setTimeout wasn't available but was latter defined
      if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
          cachedSetTimeout = setTimeout;
          return setTimeout(fun, 0);
      }
      try {
          // when when somebody has screwed with setTimeout but no I.E. maddness
          return cachedSetTimeout(fun, 0);
      } catch(e){
          try {
              // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
              return cachedSetTimeout.call(null, fun, 0);
          } catch(e){
              // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
              return cachedSetTimeout.call(this, fun, 0);
          }
      }
  
  
  }
  function runClearTimeout(marker) {
      if (cachedClearTimeout === clearTimeout) {
          //normal enviroments in sane situations
          return clearTimeout(marker);
      }
      // if clearTimeout wasn't available but was latter defined
      if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
          cachedClearTimeout = clearTimeout;
          return clearTimeout(marker);
      }
      try {
          // when when somebody has screwed with setTimeout but no I.E. maddness
          return cachedClearTimeout(marker);
      } catch (e){
          try {
              // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
              return cachedClearTimeout.call(null, marker);
          } catch (e){
              // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
              // Some versions of I.E. have different rules for clearTimeout vs setTimeout
              return cachedClearTimeout.call(this, marker);
          }
      }
  
  
  
  }
  var queue = [];
  var draining = false;
  var currentQueue;
  var queueIndex = -1;
  
  function cleanUpNextTick() {
      if (!draining || !currentQueue) {
          return;
      }
      draining = false;
      if (currentQueue.length) {
          queue = currentQueue.concat(queue);
      } else {
          queueIndex = -1;
      }
      if (queue.length) {
          drainQueue();
      }
  }
  
  function drainQueue() {
      if (draining) {
          return;
      }
      var timeout = runTimeout(cleanUpNextTick);
      draining = true;
  
      var len = queue.length;
      while(len) {
          currentQueue = queue;
          queue = [];
          while (++queueIndex < len) {
              if (currentQueue) {
                  currentQueue[queueIndex].run();
              }
          }
          queueIndex = -1;
          len = queue.length;
      }
      currentQueue = null;
      draining = false;
      runClearTimeout(timeout);
  }
  
  process.nextTick = function (fun) {
      var args = new Array(arguments.length - 1);
      if (arguments.length > 1) {
          for (var i = 1; i < arguments.length; i++) {
              args[i - 1] = arguments[i];
          }
      }
      queue.push(new Item(fun, args));
      if (queue.length === 1 && !draining) {
          runTimeout(drainQueue);
      }
  };
  
  // v8 likes predictible objects
  function Item(fun, array) {
      this.fun = fun;
      this.array = array;
  }
  Item.prototype.run = function () {
      this.fun.apply(null, this.array);
  };
  process.title = 'browser';
  process.browser = true;
  process.env = {};
  process.argv = [];
  process.version = ''; // empty string to avoid regexp issues
  process.versions = {};
  
  function noop() {}
  
  process.on = noop;
  process.addListener = noop;
  process.once = noop;
  process.off = noop;
  process.removeListener = noop;
  process.removeAllListeners = noop;
  process.emit = noop;
  process.prependListener = noop;
  process.prependOnceListener = noop;
  
  process.listeners = function (name) { return [] }
  
  process.binding = function (name) {
      throw new Error('process.binding is not supported');
  };
  
  process.cwd = function () { return '/' };
  process.chdir = function (dir) {
      throw new Error('process.chdir is not supported');
  };
  process.umask = function() { return 0; };
  
  },{}],5:[function(require,module,exports){
  /**
   * @fileOverview
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  /**
   * If you use roslib in a browser, all the classes will be exported to a global variable called ROSLIB.
   *
   * If you use nodejs, this is the variable you get when you require('roslib')
   */
  var ROSLIB = this.ROSLIB || {
    REVISION : '1.0.1'
  };
  
  var assign = require('object-assign');
  
  // Add core components
  assign(ROSLIB, require('./core'));
  
  assign(ROSLIB, require('./actionlib'));
  
  assign(ROSLIB, require('./math'));
  
  assign(ROSLIB, require('./tf'));
  
  assign(ROSLIB, require('./urdf'));
  
  module.exports = ROSLIB;
  
  },{"./actionlib":11,"./core":20,"./math":25,"./tf":28,"./urdf":40,"object-assign":3}],6:[function(require,module,exports){
  (function (global){
  global.ROSLIB = require('./RosLib');
  }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  },{"./RosLib":5}],7:[function(require,module,exports){
  /**
   * @fileOverview
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var Topic = require('../core/Topic');
  var Message = require('../core/Message');
  var EventEmitter2 = require('eventemitter2').EventEmitter2;
  
  /**
   * An actionlib action client.
   *
   * Emits the following events:
   *  * 'timeout' - if a timeout occurred while sending a goal
   *  * 'status' - the status messages received from the action server
   *  * 'feedback' -  the feedback messages received from the action server
   *  * 'result' - the result returned from the action server
   *
   *  @constructor
   *  @param options - object with following keys:
   *   * ros - the ROSLIB.Ros connection handle
   *   * serverName - the action server name, like /fibonacci
   *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'
   *   * timeout - the timeout length when connecting to the action server
   */
  function ActionClient(options) {
    var that = this;
    options = options || {};
    this.ros = options.ros;
    this.serverName = options.serverName;
    this.actionName = options.actionName;
    this.timeout = options.timeout;
    this.omitFeedback = options.omitFeedback;
    this.omitStatus = options.omitStatus;
    this.omitResult = options.omitResult;
    this.goals = {};
  
    // flag to check if a status has been received
    var receivedStatus = false;
  
    // create the topics associated with actionlib
    this.feedbackListener = new Topic({
      ros : this.ros,
      name : this.serverName + '/feedback',
      messageType : this.actionName + 'Feedback'
    });
  
    this.statusListener = new Topic({
      ros : this.ros,
      name : this.serverName + '/status',
      messageType : 'actionlib_msgs/GoalStatusArray'
    });
  
    this.resultListener = new Topic({
      ros : this.ros,
      name : this.serverName + '/result',
      messageType : this.actionName + 'Result'
    });
  
    this.goalTopic = new Topic({
      ros : this.ros,
      name : this.serverName + '/goal',
      messageType : this.actionName + 'Goal'
    });
  
    this.cancelTopic = new Topic({
      ros : this.ros,
      name : this.serverName + '/cancel',
      messageType : 'actionlib_msgs/GoalID'
    });
  
    // advertise the goal and cancel topics
    this.goalTopic.advertise();
    this.cancelTopic.advertise();
  
    // subscribe to the status topic
    if (!this.omitStatus) {
      this.statusListener.subscribe(function(statusMessage) {
        receivedStatus = true;
        statusMessage.status_list.forEach(function(status) {
          var goal = that.goals[status.goal_id.id];
          if (goal) {
            goal.emit('status', status);
          }
        });
      });
    }
  
    // subscribe the the feedback topic
    if (!this.omitFeedback) {
      this.feedbackListener.subscribe(function(feedbackMessage) {
        var goal = that.goals[feedbackMessage.status.goal_id.id];
        if (goal) {
          goal.emit('status', feedbackMessage.status);
          goal.emit('feedback', feedbackMessage.feedback);
        }
      });
    }
  
    // subscribe to the result topic
    if (!this.omitResult) {
      this.resultListener.subscribe(function(resultMessage) {
        var goal = that.goals[resultMessage.status.goal_id.id];
  
        if (goal) {
          goal.emit('status', resultMessage.status);
          goal.emit('result', resultMessage.result);
        }
      });
    }
  
    // If timeout specified, emit a 'timeout' event if the action server does not respond
    if (this.timeout) {
      setTimeout(function() {
        if (!receivedStatus) {
          that.emit('timeout');
        }
      }, this.timeout);
    }
  }
  
  ActionClient.prototype.__proto__ = EventEmitter2.prototype;
  
  /**
   * Cancel all goals associated with this ActionClient.
   */
  ActionClient.prototype.cancel = function() {
    var cancelMessage = new Message();
    this.cancelTopic.publish(cancelMessage);
  };
  
  /**
   * Unsubscribe and unadvertise all topics associated with this ActionClient.
   */
  ActionClient.prototype.dispose = function() {
    this.goalTopic.unadvertise();
    this.cancelTopic.unadvertise();
    if (!this.omitStatus) {this.statusListener.unsubscribe();}
    if (!this.omitFeedback) {this.feedbackListener.unsubscribe();}
    if (!this.omitResult) {this.resultListener.unsubscribe();}
  };
  
  module.exports = ActionClient;
  
  },{"../core/Message":12,"../core/Topic":19,"eventemitter2":2}],8:[function(require,module,exports){
  /**
   * @fileOverview
   * @author Justin Young - justin@oodar.com.au
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var Topic = require('../core/Topic');
  var Message = require('../core/Message');
  var EventEmitter2 = require('eventemitter2').EventEmitter2;
  
  /**
   * An actionlib action listener
   *
   * Emits the following events:
   *  * 'status' - the status messages received from the action server
   *  * 'feedback' -  the feedback messages received from the action server
   *  * 'result' - the result returned from the action server
   *
   *  @constructor
   *  @param options - object with following keys:
   *   * ros - the ROSLIB.Ros connection handle
   *   * serverName - the action server name, like /fibonacci
   *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'
   */
  function ActionListener(options) {
    var that = this;
    options = options || {};
    this.ros = options.ros;
    this.serverName = options.serverName;
    this.actionName = options.actionName;
    this.timeout = options.timeout;
    this.omitFeedback = options.omitFeedback;
    this.omitStatus = options.omitStatus;
    this.omitResult = options.omitResult;
  
  
    // create the topics associated with actionlib
    var goalListener = new Topic({
      ros : this.ros,
      name : this.serverName + '/goal',
      messageType : this.actionName + 'Goal'
    });
  
    var feedbackListener = new Topic({
      ros : this.ros,
      name : this.serverName + '/feedback',
      messageType : this.actionName + 'Feedback'
    });
  
    var statusListener = new Topic({
      ros : this.ros,
      name : this.serverName + '/status',
      messageType : 'actionlib_msgs/GoalStatusArray'
    });
  
    var resultListener = new Topic({
      ros : this.ros,
      name : this.serverName + '/result',
      messageType : this.actionName + 'Result'
    });
  
    goalListener.subscribe(function(goalMessage) {
        that.emit('goal', goalMessage);
    });
  
    statusListener.subscribe(function(statusMessage) {
        statusMessage.status_list.forEach(function(status) {
            that.emit('status', status);
        });
    });
  
    feedbackListener.subscribe(function(feedbackMessage) {
        that.emit('status', feedbackMessage.status);
        that.emit('feedback', feedbackMessage.feedback);
    });
  
    // subscribe to the result topic
    resultListener.subscribe(function(resultMessage) {
        that.emit('status', resultMessage.status);
        that.emit('result', resultMessage.result);
    });
  
  }
  
  ActionListener.prototype.__proto__ = EventEmitter2.prototype;
  
  module.exports = ActionListener;
  
  },{"../core/Message":12,"../core/Topic":19,"eventemitter2":2}],9:[function(require,module,exports){
  /**
   * @fileOverview
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var Message = require('../core/Message');
  var EventEmitter2 = require('eventemitter2').EventEmitter2;
  
  /**
   * An actionlib goal goal is associated with an action server.
   *
   * Emits the following events:
   *  * 'timeout' - if a timeout occurred while sending a goal
   *
   *  @constructor
   *  @param object with following keys:
   *   * actionClient - the ROSLIB.ActionClient to use with this goal
   *   * goalMessage - The JSON object containing the goal for the action server
   */
  function Goal(options) {
    var that = this;
    this.actionClient = options.actionClient;
    this.goalMessage = options.goalMessage;
    this.isFinished = false;
  
    // Used to create random IDs
    var date = new Date();
  
    // Create a random ID
    this.goalID = 'goal_' + Math.random() + '_' + date.getTime();
    // Fill in the goal message
    this.goalMessage = new Message({
      goal_id : {
        stamp : {
          secs : 0,
          nsecs : 0
        },
        id : this.goalID
      },
      goal : this.goalMessage
    });
  
    this.on('status', function(status) {
      that.status = status;
    });
  
    this.on('result', function(result) {
      that.isFinished = true;
      that.result = result;
    });
  
    this.on('feedback', function(feedback) {
      that.feedback = feedback;
    });
  
    // Add the goal
    this.actionClient.goals[this.goalID] = this;
  }
  
  Goal.prototype.__proto__ = EventEmitter2.prototype;
  
  /**
   * Send the goal to the action server.
   *
   * @param timeout (optional) - a timeout length for the goal's result
   */
  Goal.prototype.send = function(timeout) {
    var that = this;
    that.actionClient.goalTopic.publish(that.goalMessage);
    if (timeout) {
      setTimeout(function() {
        if (!that.isFinished) {
          that.emit('timeout');
        }
      }, timeout);
    }
  };
  
  /**
   * Cancel the current goal.
   */
  Goal.prototype.cancel = function() {
    var cancelMessage = new Message({
      id : this.goalID
    });
    this.actionClient.cancelTopic.publish(cancelMessage);
  };
  
  module.exports = Goal;
  },{"../core/Message":12,"eventemitter2":2}],10:[function(require,module,exports){
  /**
   * @fileOverview
   * @author Laura Lindzey - lindzey@gmail.com
   */
  
  var Topic = require('../core/Topic');
  var Message = require('../core/Message');
  var EventEmitter2 = require('eventemitter2').EventEmitter2;
  
  /**
   * An actionlib action server client.
   *
   * Emits the following events:
   *  * 'goal' - goal sent by action client
   *  * 'cancel' - action client has canceled the request
   *
   *  @constructor
   *  @param options - object with following keys:
   *   * ros - the ROSLIB.Ros connection handle
   *   * serverName - the action server name, like /fibonacci
   *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'
   */
  
  function SimpleActionServer(options) {
      var that = this;
      options = options || {};
      this.ros = options.ros;
      this.serverName = options.serverName;
      this.actionName = options.actionName;
  
      // create and advertise publishers
      this.feedbackPublisher = new Topic({
          ros : this.ros,
          name : this.serverName + '/feedback',
          messageType : this.actionName + 'Feedback'
      });
      this.feedbackPublisher.advertise();
  
      var statusPublisher = new Topic({
          ros : this.ros,
          name : this.serverName + '/status',
          messageType : 'actionlib_msgs/GoalStatusArray'
      });
      statusPublisher.advertise();
  
      this.resultPublisher = new Topic({
          ros : this.ros,
          name : this.serverName + '/result',
          messageType : this.actionName + 'Result'
      });
      this.resultPublisher.advertise();
  
      // create and subscribe to listeners
      var goalListener = new Topic({
          ros : this.ros,
          name : this.serverName + '/goal',
          messageType : this.actionName + 'Goal'
      });
  
      var cancelListener = new Topic({
          ros : this.ros,
          name : this.serverName + '/cancel',
          messageType : 'actionlib_msgs/GoalID'
      });
  
      // Track the goals and their status in order to publish status...
      this.statusMessage = new Message({
          header : {
              stamp : {secs : 0, nsecs : 100},
              frame_id : ''
          },
          status_list : []
      });
  
      // needed for handling preemption prompted by a new goal being received
      this.currentGoal = null; // currently tracked goal
      this.nextGoal = null; // the one that'll be preempting
  
      goalListener.subscribe(function(goalMessage) {
          
      if(that.currentGoal) {
              that.nextGoal = goalMessage;
              // needs to happen AFTER rest is set up
              that.emit('cancel');
      } else {
              that.statusMessage.status_list = [{goal_id : goalMessage.goal_id, status : 1}];
              that.currentGoal = goalMessage;
              that.emit('goal', goalMessage.goal);
      }
      });
  
      // helper function for determing ordering of timestamps
      // returns t1 < t2
      var isEarlier = function(t1, t2) {
          if(t1.secs > t2.secs) {
              return false;
          } else if(t1.secs < t2.secs) {
              return true;
          } else if(t1.nsecs < t2.nsecs) {
              return true;
          } else {
              return false;
          }
      };
  
      // TODO: this may be more complicated than necessary, since I'm
      // not sure if the callbacks can ever wind up with a scenario
      // where we've been preempted by a next goal, it hasn't finished
      // processing, and then we get a cancel message
      cancelListener.subscribe(function(cancelMessage) {
  
          // cancel ALL goals if both empty
          if(cancelMessage.stamp.secs === 0 && cancelMessage.stamp.secs === 0 && cancelMessage.id === '') {
              that.nextGoal = null;
              if(that.currentGoal) {
                  that.emit('cancel');
              }
          } else { // treat id and stamp independently
              if(that.currentGoal && cancelMessage.id === that.currentGoal.goal_id.id) {
                  that.emit('cancel');
              } else if(that.nextGoal && cancelMessage.id === that.nextGoal.goal_id.id) {
                  that.nextGoal = null;
              }
  
              if(that.nextGoal && isEarlier(that.nextGoal.goal_id.stamp,
                                            cancelMessage.stamp)) {
                  that.nextGoal = null;
              }
              if(that.currentGoal && isEarlier(that.currentGoal.goal_id.stamp,
                                               cancelMessage.stamp)) {
                  
                  that.emit('cancel');
              }
          }
      });
  
      // publish status at pseudo-fixed rate; required for clients to know they've connected
      var statusInterval = setInterval( function() {
          var currentTime = new Date();
          var secs = Math.floor(currentTime.getTime()/1000);
          var nsecs = Math.round(1000000000*(currentTime.getTime()/1000-secs));
          that.statusMessage.header.stamp.secs = secs;
          that.statusMessage.header.stamp.nsecs = nsecs;
          statusPublisher.publish(that.statusMessage);
      }, 500); // publish every 500ms
  
  }
  
  SimpleActionServer.prototype.__proto__ = EventEmitter2.prototype;
  
  /**
  *  Set action state to succeeded and return to client
  */
  
  SimpleActionServer.prototype.setSucceeded = function(result2) {
      
  
      var resultMessage = new Message({
          status : {goal_id : this.currentGoal.goal_id, status : 3},
          result : result2
      });
      this.resultPublisher.publish(resultMessage);
  
      this.statusMessage.status_list = [];
      if(this.nextGoal) {
          this.currentGoal = this.nextGoal;
          this.nextGoal = null;
          this.emit('goal', this.currentGoal.goal);
      } else {
          this.currentGoal = null;
      }
  };
  
  /**
  *  Function to send feedback
  */
  
  SimpleActionServer.prototype.sendFeedback = function(feedback2) {
  
      var feedbackMessage = new Message({
          status : {goal_id : this.currentGoal.goal_id, status : 1},
          feedback : feedback2
      });
      this.feedbackPublisher.publish(feedbackMessage);
  };
  
  /**
  *  Handle case where client requests preemption
  */
  
  SimpleActionServer.prototype.setPreempted = function() {
  
      this.statusMessage.status_list = [];
      var resultMessage = new Message({
          status : {goal_id : this.currentGoal.goal_id, status : 2},
      });
      this.resultPublisher.publish(resultMessage);
  
      if(this.nextGoal) {
          this.currentGoal = this.nextGoal;
          this.nextGoal = null;
          this.emit('goal', this.currentGoal.goal);
      } else {
          this.currentGoal = null;
      }
  };
  
  module.exports = SimpleActionServer;
  },{"../core/Message":12,"../core/Topic":19,"eventemitter2":2}],11:[function(require,module,exports){
  var Ros = require('../core/Ros');
  var mixin = require('../mixin');
  
  var action = module.exports = {
      ActionClient: require('./ActionClient'),
      ActionListener: require('./ActionListener'),
      Goal: require('./Goal'),
      SimpleActionServer: require('./SimpleActionServer')
  };
  
  mixin(Ros, ['ActionClient', 'SimpleActionServer'], action);
  
  },{"../core/Ros":14,"../mixin":26,"./ActionClient":7,"./ActionListener":8,"./Goal":9,"./SimpleActionServer":10}],12:[function(require,module,exports){
  /**
   * @fileoverview
   * @author Brandon Alexander - baalexander@gmail.com
   */
  
  var assign = require('object-assign');
  
  /**
   * Message objects are used for publishing and subscribing to and from topics.
   *
   * @constructor
   * @param values - object matching the fields defined in the .msg definition file
   */
  function Message(values) {
    assign(this, values);
  }
  
  module.exports = Message;
  },{"object-assign":3}],13:[function(require,module,exports){
  /**
   * @fileoverview
   * @author Brandon Alexander - baalexander@gmail.com
   */
  
  var Service = require('./Service');
  var ServiceRequest = require('./ServiceRequest');
  
  /**
   * A ROS parameter.
   *
   * @constructor
   * @param options - possible keys include:
   *   * ros - the ROSLIB.Ros connection handle
   *   * name - the param name, like max_vel_x
   */
  function Param(options) {
    options = options || {};
    this.ros = options.ros;
    this.name = options.name;
  }
  
  /**
   * Fetches the value of the param.
   *
   * @param callback - function with the following params:
   *  * value - the value of the param from ROS.
   */
  Param.prototype.get = function(callback) {
    var paramClient = new Service({
      ros : this.ros,
      name : '/rosapi/get_param',
      serviceType : 'rosapi/GetParam'
    });
  
    var request = new ServiceRequest({
      name : this.name
    });
  
    paramClient.callService(request, function(result) {
      var value = JSON.parse(result.value);
      callback(value);
    });
  };
  
  /**
   * Sets the value of the param in ROS.
   *
   * @param value - value to set param to.
   */
  Param.prototype.set = function(value, callback) {
    var paramClient = new Service({
      ros : this.ros,
      name : '/rosapi/set_param',
      serviceType : 'rosapi/SetParam'
    });
  
    var request = new ServiceRequest({
      name : this.name,
      value : JSON.stringify(value)
    });
  
    paramClient.callService(request, callback);
  };
  
  /**
   * Delete this parameter on the ROS server.
   */
  Param.prototype.delete = function(callback) {
    var paramClient = new Service({
      ros : this.ros,
      name : '/rosapi/delete_param',
      serviceType : 'rosapi/DeleteParam'
    });
  
    var request = new ServiceRequest({
      name : this.name
    });
  
    paramClient.callService(request, callback);
  };
  
  module.exports = Param;
  },{"./Service":15,"./ServiceRequest":16}],14:[function(require,module,exports){
  /**
   * @fileoverview
   * @author Brandon Alexander - baalexander@gmail.com
   */
  
  var WebSocket = require('ws');
  var socketAdapter = require('./SocketAdapter.js');
  
  var Service = require('./Service');
  var ServiceRequest = require('./ServiceRequest');
  
  var assign = require('object-assign');
  var EventEmitter2 = require('eventemitter2').EventEmitter2;
  
  /**
   * Manages connection to the server and all interactions with ROS.
   *
   * Emits the following events:
   *  * 'error' - there was an error with ROS
   *  * 'connection' - connected to the WebSocket server
   *  * 'close' - disconnected to the WebSocket server
   *  * <topicName> - a message came from rosbridge with the given topic name
   *  * <serviceID> - a service response came from rosbridge with the given ID
   *
   * @constructor
   * @param options - possible keys include: <br>
   *   * url (optional) - (can be specified later with `connect`) the WebSocket URL for rosbridge or the node server url to connect using socket.io (if socket.io exists in the page) <br>
   *   * groovyCompatibility - don't use interfaces that changed after the last groovy release or rosbridge_suite and related tools (defaults to true)
   *   * transportLibrary (optional) - one of 'websocket' (default), 'socket.io' or RTCPeerConnection instance controlling how the connection is created in `connect`.
   *   * transportOptions (optional) - the options to use use when creating a connection. Currently only used if `transportLibrary` is RTCPeerConnection.
   */
  function Ros(options) {
    options = options || {};
    this.socket = null;
    this.idCounter = 0;
    this.isConnected = false;
    this.transportLibrary = options.transportLibrary || 'websocket';
    this.transportOptions = options.transportOptions || {};
  
    if (typeof options.groovyCompatibility === 'undefined') {
      this.groovyCompatibility = true;
    }
    else {
      this.groovyCompatibility = options.groovyCompatibility;
    }
  
    // Sets unlimited event listeners.
    this.setMaxListeners(0);
  
    // begin by checking if a URL was given
    if (options.url) {
      this.connect(options.url);
    }
  }
  
  Ros.prototype.__proto__ = EventEmitter2.prototype;
  
  /**
   * Connect to the specified WebSocket.
   *
   * @param url - WebSocket URL or RTCDataChannel label for Rosbridge
   */
  Ros.prototype.connect = function(url) {
    if (this.transportLibrary === 'socket.io') {
      this.socket = assign(io(url, {'force new connection': true}), socketAdapter(this));
      this.socket.on('connect', this.socket.onopen);
      this.socket.on('data', this.socket.onmessage);
      this.socket.on('close', this.socket.onclose);
      this.socket.on('error', this.socket.onerror);
    } else if (this.transportLibrary.constructor.name === 'RTCPeerConnection') {
      this.socket = assign(this.transportLibrary.createDataChannel(url, this.transportOptions), socketAdapter(this));
    } else {
      if (!this.socket || this.socket.readyState === WebSocket.CLOSED) {
        var sock = new WebSocket(url);
        sock.binaryType = 'arraybuffer';
        this.socket = assign(sock, socketAdapter(this));
      }
    }
  
  };
  
  /**
   * Disconnect from the WebSocket server.
   */
  Ros.prototype.close = function() {
    if (this.socket) {
      this.socket.close();
    }
  };
  
  /**
   * Sends an authorization request to the server.
   *
   * @param mac - MAC (hash) string given by the trusted source.
   * @param client - IP of the client.
   * @param dest - IP of the destination.
   * @param rand - Random string given by the trusted source.
   * @param t - Time of the authorization request.
   * @param level - User level as a string given by the client.
   * @param end - End time of the client's session.
   */
  Ros.prototype.authenticate = function(mac, client, dest, rand, t, level, end) {
    // create the request
    var auth = {
      op : 'auth',
      mac : mac,
      client : client,
      dest : dest,
      rand : rand,
      t : t,
      level : level,
      end : end
    };
    // send the request
    this.callOnConnection(auth);
  };
  
  /**
   * Sends the message over the WebSocket, but queues the message up if not yet
   * connected.
   */
  Ros.prototype.callOnConnection = function(message) {
    var that = this;
    var messageJson = JSON.stringify(message);
    var emitter = null;
    if (this.transportLibrary === 'socket.io') {
      emitter = function(msg){that.socket.emit('operation', msg);};
    } else {
      emitter = function(msg){that.socket.send(msg);};
    }
  
    if (!this.isConnected) {
      that.once('connection', function() {
        emitter(messageJson);
      });
    } else {
      emitter(messageJson);
    }
  };
  
  /**
   * Sends a set_level request to the server
   *
   * @param level - Status level (none, error, warning, info)
   * @param id - Optional: Operation ID to change status level on
   */
  Ros.prototype.setStatusLevel = function(level, id){
    var levelMsg = {
      op: 'set_level',
      level: level,
      id: id
    };
  
    this.callOnConnection(levelMsg);
  };
  
  /**
   * Retrieves Action Servers in ROS as an array of string
   *
   *   * actionservers - Array of action server names
   */
  Ros.prototype.getActionServers = function(callback, failedCallback) {
    var getActionServers = new Service({
      ros : this,
      name : '/rosapi/action_servers',
      serviceType : 'rosapi/GetActionServers'
    });
  
    var request = new ServiceRequest({});
    if (typeof failedCallback === 'function'){
      getActionServers.callService(request,
        function(result) {
          callback(result.action_servers);
        },
        function(message){
          failedCallback(message);
        }
      );
    }else{
      getActionServers.callService(request, function(result) {
        callback(result.action_servers);
      });
    }
  };
  
  /**
   * Retrieves list of topics in ROS as an array.
   *
   * @param callback function with params:
   *   * topics - Array of topic names
   */
  Ros.prototype.getTopics = function(callback, failedCallback) {
    var topicsClient = new Service({
      ros : this,
      name : '/rosapi/topics',
      serviceType : 'rosapi/Topics'
    });
  
    var request = new ServiceRequest();
    if (typeof failedCallback === 'function'){
      topicsClient.callService(request,
        function(result) {
          callback(result);
        },
        function(message){
          failedCallback(message);
        }
      );
    }else{
      topicsClient.callService(request, function(result) {
        callback(result);
      });
    }
  };
  
  /**
   * Retrieves Topics in ROS as an array as specific type
   *
   * @param topicType topic type to find:
   * @param callback function with params:
   *   * topics - Array of topic names
   */
  Ros.prototype.getTopicsForType = function(topicType, callback, failedCallback) {
    var topicsForTypeClient = new Service({
      ros : this,
      name : '/rosapi/topics_for_type',
      serviceType : 'rosapi/TopicsForType'
    });
  
    var request = new ServiceRequest({
      type: topicType
    });
    if (typeof failedCallback === 'function'){
      topicsForTypeClient.callService(request,
        function(result) {
          callback(result.topics);
        },
        function(message){
          failedCallback(message);
        }
      );
    }else{
      topicsForTypeClient.callService(request, function(result) {
        callback(result.topics);
      });
    }
  };
  
  /**
   * Retrieves list of active service names in ROS.
   *
   * @param callback - function with the following params:
   *   * services - array of service names
   */
  Ros.prototype.getServices = function(callback, failedCallback) {
    var servicesClient = new Service({
      ros : this,
      name : '/rosapi/services',
      serviceType : 'rosapi/Services'
    });
  
    var request = new ServiceRequest();
    if (typeof failedCallback === 'function'){
      servicesClient.callService(request,
        function(result) {
          callback(result.services);
        },
        function(message) {
          failedCallback(message);
        }
      );
    }else{
      servicesClient.callService(request, function(result) {
        callback(result.services);
      });
    }
  };
  
  /**
   * Retrieves list of services in ROS as an array as specific type
   *
   * @param serviceType service type to find:
   * @param callback function with params:
   *   * topics - Array of service names
   */
  Ros.prototype.getServicesForType = function(serviceType, callback, failedCallback) {
    var servicesForTypeClient = new Service({
      ros : this,
      name : '/rosapi/services_for_type',
      serviceType : 'rosapi/ServicesForType'
    });
  
    var request = new ServiceRequest({
      type: serviceType
    });
    if (typeof failedCallback === 'function'){
      servicesForTypeClient.callService(request,
        function(result) {
          callback(result.services);
        },
        function(message) {
          failedCallback(message);
        }
      );
    }else{
      servicesForTypeClient.callService(request, function(result) {
        callback(result.services);
      });
    }
  };
  
  /**
   * Retrieves a detail of ROS service request.
   *
   * @param service name of service:
   * @param callback - function with params:
   *   * type - String of the service type
   */
  Ros.prototype.getServiceRequestDetails = function(type, callback, failedCallback) {
    var serviceTypeClient = new Service({
      ros : this,
      name : '/rosapi/service_request_details',
      serviceType : 'rosapi/ServiceRequestDetails'
    });
    var request = new ServiceRequest({
      type: type
    });
  
    if (typeof failedCallback === 'function'){
      serviceTypeClient.callService(request,
        function(result) {
          callback(result);
        },
        function(message){
          failedCallback(message);
        }
      );
    }else{
      serviceTypeClient.callService(request, function(result) {
        callback(result);
      });
    }
  };
  
  /**
   * Retrieves a detail of ROS service request.
   *
   * @param service name of service:
   * @param callback - function with params:
   *   * type - String of the service type
   */
  Ros.prototype.getServiceResponseDetails = function(type, callback, failedCallback) {
    var serviceTypeClient = new Service({
      ros : this,
      name : '/rosapi/service_response_details',
      serviceType : 'rosapi/ServiceResponseDetails'
    });
    var request = new ServiceRequest({
      type: type
    });
  
    if (typeof failedCallback === 'function'){
      serviceTypeClient.callService(request,
        function(result) {
          callback(result);
        },
        function(message){
          failedCallback(message);
        }
      );
    }else{
      serviceTypeClient.callService(request, function(result) {
        callback(result);
      });
    }
  };
  
  /**
   * Retrieves list of active node names in ROS.
   *
   * @param callback - function with the following params:
   *   * nodes - array of node names
   */
  Ros.prototype.getNodes = function(callback, failedCallback) {
    var nodesClient = new Service({
      ros : this,
      name : '/rosapi/nodes',
      serviceType : 'rosapi/Nodes'
    });
  
    var request = new ServiceRequest();
    if (typeof failedCallback === 'function'){
      nodesClient.callService(request,
        function(result) {
          callback(result.nodes);
        },
        function(message) {
          failedCallback(message);
        }
      );
    }else{
      nodesClient.callService(request, function(result) {
        callback(result.nodes);
      });
    }
  };
  
  /**
    * Retrieves list subscribed topics, publishing topics and services of a specific node
    *
    * @param node name of the node:
    * @param callback - function with params:
    *   * publications - array of published topic names
    *   * subscriptions - array of subscribed topic names
    *   * services - array of service names hosted
    */
  Ros.prototype.getNodeDetails = function(node, callback, failedCallback) {
    var nodesClient = new Service({
      ros : this,
      name : '/rosapi/node_details',
      serviceType : 'rosapi/NodeDetails'
    });
  
    var request = new ServiceRequest({
      node: node
    });
    if (typeof failedCallback === 'function'){
      nodesClient.callService(request,
        function(result) {
          callback(result.subscribing, result.publishing, result.services);
        },
        function(message) {
          failedCallback(message);
        }
      );
    } else {
      nodesClient.callService(request, function(result) {
        callback(result);
      });
    }
  };
  
  /**
   * Retrieves list of param names from the ROS Parameter Server.
   *
   * @param callback function with params:
   *  * params - array of param names.
   */
  Ros.prototype.getParams = function(callback, failedCallback) {
    var paramsClient = new Service({
      ros : this,
      name : '/rosapi/get_param_names',
      serviceType : 'rosapi/GetParamNames'
    });
    var request = new ServiceRequest();
    if (typeof failedCallback === 'function'){
      paramsClient.callService(request,
        function(result) {
          callback(result.names);
        },
        function(message){
          failedCallback(message);
        }
      );
    }else{
      paramsClient.callService(request, function(result) {
        callback(result.names);
      });
    }
  };
  
  /**
   * Retrieves a type of ROS topic.
   *
   * @param topic name of the topic:
   * @param callback - function with params:
   *   * type - String of the topic type
   */
  Ros.prototype.getTopicType = function(topic, callback, failedCallback) {
    var topicTypeClient = new Service({
      ros : this,
      name : '/rosapi/topic_type',
      serviceType : 'rosapi/TopicType'
    });
    var request = new ServiceRequest({
      topic: topic
    });
  
    if (typeof failedCallback === 'function'){
      topicTypeClient.callService(request,
        function(result) {
          callback(result.type);
        },
        function(message){
          failedCallback(message);
        }
      );
    }else{
      topicTypeClient.callService(request, function(result) {
        callback(result.type);
      });
    }
  };
  
  /**
   * Retrieves a type of ROS service.
   *
   * @param service name of service:
   * @param callback - function with params:
   *   * type - String of the service type
   */
  Ros.prototype.getServiceType = function(service, callback, failedCallback) {
    var serviceTypeClient = new Service({
      ros : this,
      name : '/rosapi/service_type',
      serviceType : 'rosapi/ServiceType'
    });
    var request = new ServiceRequest({
      service: service
    });
  
    if (typeof failedCallback === 'function'){
      serviceTypeClient.callService(request,
        function(result) {
          callback(result.type);
        },
        function(message){
          failedCallback(message);
        }
      );
    }else{
      serviceTypeClient.callService(request, function(result) {
        callback(result.type);
      });
    }
  };
  
  /**
   * Retrieves a detail of ROS message.
   *
   * @param callback - function with params:
   *   * details - Array of the message detail
   * @param message - String of a topic type
   */
  Ros.prototype.getMessageDetails = function(message, callback, failedCallback) {
    var messageDetailClient = new Service({
      ros : this,
      name : '/rosapi/message_details',
      serviceType : 'rosapi/MessageDetails'
    });
    var request = new ServiceRequest({
      type: message
    });
  
    if (typeof failedCallback === 'function'){
      messageDetailClient.callService(request,
        function(result) {
          callback(result.typedefs);
        },
        function(message){
          failedCallback(message);
        }
      );
    }else{
      messageDetailClient.callService(request, function(result) {
        callback(result.typedefs);
      });
    }
  };
  
  /**
   * Decode a typedefs into a dictionary like `rosmsg show foo/bar`
   *
   * @param defs - array of type_def dictionary
   */
  Ros.prototype.decodeTypeDefs = function(defs) {
    var that = this;
  
    // calls itself recursively to resolve type definition using hints.
    var decodeTypeDefsRec = function(theType, hints) {
      var typeDefDict = {};
      for (var i = 0; i < theType.fieldnames.length; i++) {
        var arrayLen = theType.fieldarraylen[i];
        var fieldName = theType.fieldnames[i];
        var fieldType = theType.fieldtypes[i];
        if (fieldType.indexOf('/') === -1) { // check the fieldType includes '/' or not
          if (arrayLen === -1) {
            typeDefDict[fieldName] = fieldType;
          }
          else {
            typeDefDict[fieldName] = [fieldType];
          }
        }
        else {
          // lookup the name
          var sub = false;
          for (var j = 0; j < hints.length; j++) {
            if (hints[j].type.toString() === fieldType.toString()) {
              sub = hints[j];
              break;
            }
          }
          if (sub) {
            var subResult = decodeTypeDefsRec(sub, hints);
            if (arrayLen === -1) {
            }
            else {
              typeDefDict[fieldName] = [subResult];
            }
          }
          else {
            that.emit('error', 'Cannot find ' + fieldType + ' in decodeTypeDefs');
          }
        }
      }
      return typeDefDict;
    };
  
    return decodeTypeDefsRec(defs[0], defs);
  };
  
  
  module.exports = Ros;
  
  },{"./Service":15,"./ServiceRequest":16,"./SocketAdapter.js":18,"eventemitter2":2,"object-assign":3,"ws":42}],15:[function(require,module,exports){
  /**
   * @fileoverview
   * @author Brandon Alexander - baalexander@gmail.com
   */
  
  var ServiceResponse = require('./ServiceResponse');
  var ServiceRequest = require('./ServiceRequest');
  var EventEmitter2 = require('eventemitter2').EventEmitter2;
  
  /**
   * A ROS service client.
   *
   * @constructor
   * @params options - possible keys include:
   *   * ros - the ROSLIB.Ros connection handle
   *   * name - the service name, like /add_two_ints
   *   * serviceType - the service type, like 'rospy_tutorials/AddTwoInts'
   */
  function Service(options) {
    options = options || {};
    this.ros = options.ros;
    this.name = options.name;
    this.serviceType = options.serviceType;
    this.isAdvertised = false;
  
    this._serviceCallback = null;
  }
  Service.prototype.__proto__ = EventEmitter2.prototype;
  /**
   * Calls the service. Returns the service response in the
   * callback. Does nothing if this service is currently advertised.
   *
   * @param request - the ROSLIB.ServiceRequest to send
   * @param callback - function with params:
   *   * response - the response from the service request
   * @param failedCallback - the callback function when the service call failed (optional). Params:
   *   * error - the error message reported by ROS
   */
  Service.prototype.callService = function(request, callback, failedCallback) {
    if (this.isAdvertised) {
      return;
    }
  
    var serviceCallId = 'call_service:' + this.name + ':' + (++this.ros.idCounter);
  
    if (callback || failedCallback) {
      this.ros.once(serviceCallId, function(message) {
        if (message.result !== undefined && message.result === false) {
          if (typeof failedCallback === 'function') {
            failedCallback(message.values);
          }
        } else if (typeof callback === 'function') {
          callback(new ServiceResponse(message.values));
        }
      });
    }
  
    var call = {
      op : 'call_service',
      id : serviceCallId,
      service : this.name,
      type: this.serviceType,
      args : request
    };
    this.ros.callOnConnection(call);
  };
  
  /**
   * Advertise the service. This turns the Service object from a client
   * into a server. The callback will be called with every request
   * that's made on this service.
   *
   * @param callback - This works similarly to the callback for a C++ service and should take the following params:
   *   * request - the service request
   *   * response - an empty dictionary. Take care not to overwrite this. Instead, only modify the values within.
   *   It should return true if the service has finished successfully,
   *   i.e. without any fatal errors.
   */
  Service.prototype.advertise = function(callback) {
    if (this.isAdvertised || typeof callback !== 'function') {
      return;
    }
  
    this._serviceCallback = callback;
    this.ros.on(this.name, this._serviceResponse.bind(this));
    this.ros.callOnConnection({
      op: 'advertise_service',
      type: this.serviceType,
      service: this.name
    });
    this.isAdvertised = true;
  };
  
  Service.prototype.unadvertise = function() {
    if (!this.isAdvertised) {
      return;
    }
    this.ros.callOnConnection({
      op: 'unadvertise_service',
      service: this.name
    });
    this.isAdvertised = false;
  };
  
  Service.prototype._serviceResponse = function(rosbridgeRequest) {
    var response = {};
    var success = this._serviceCallback(rosbridgeRequest.args, response);
  
    var call = {
      op: 'service_response',
      service: this.name,
      values: new ServiceResponse(response),
      result: success
    };
  
    if (rosbridgeRequest.id) {
      call.id = rosbridgeRequest.id;
    }
  
    this.ros.callOnConnection(call);
  };
  
  module.exports = Service;
  
  },{"./ServiceRequest":16,"./ServiceResponse":17,"eventemitter2":2}],16:[function(require,module,exports){
  /**
   * @fileoverview
   * @author Brandon Alexander - balexander@willowgarage.com
   */
  
  var assign = require('object-assign');
  
  /**
   * A ServiceRequest is passed into the service call.
   *
   * @constructor
   * @param values - object matching the fields defined in the .srv definition file
   */
  function ServiceRequest(values) {
    assign(this, values);
  }
  
  module.exports = ServiceRequest;
  },{"object-assign":3}],17:[function(require,module,exports){
  /**
   * @fileoverview
   * @author Brandon Alexander - balexander@willowgarage.com
   */
  
  var assign = require('object-assign');
  
  /**
   * A ServiceResponse is returned from the service call.
   *
   * @constructor
   * @param values - object matching the fields defined in the .srv definition file
   */
  function ServiceResponse(values) {
    assign(this, values);
  }
  
  module.exports = ServiceResponse;
  },{"object-assign":3}],18:[function(require,module,exports){
  /**
   * Socket event handling utilities for handling events on either
   * WebSocket and TCP sockets
   *
   * Note to anyone reviewing this code: these functions are called
   * in the context of their parent object, unless bound
   * @fileOverview
   */
  'use strict';
  
  var decompressPng = require('../util/decompressPng');
  var CBOR = require('cbor-js');
  var typedArrayTagger = require('../util/cborTypedArrayTags');
  var WebSocket = require('ws');
  var BSON = null;
  if(typeof bson !== 'undefined'){
      BSON = bson().BSON;
  }
  
  /**
   * Events listeners for a WebSocket or TCP socket to a JavaScript
   * ROS Client. Sets up Messages for a given topic to trigger an
   * event on the ROS client.
   *
   * @namespace SocketAdapter
   * @private
   */
  function SocketAdapter(client) {
    function handleMessage(message) {
      if (message.op === 'publish') {
        client.emit(message.topic, message.msg);
      } else if (message.op === 'service_response') {
        client.emit(message.id, message);
      } else if (message.op === 'call_service') {
        client.emit(message.service, message);
      } else if(message.op === 'status'){
        if(message.id){
          client.emit('status:'+message.id, message);
        } else {
          client.emit('status', message);
        }
      }
    }
  
    function handlePng(message, callback) {
      if (message.op === 'png') {
        decompressPng(message.data, callback);
      } else {
        callback(message);
      }
    }
  
    function decodeBSON(data, callback) {
      if (!BSON) {
        throw 'Cannot process BSON encoded message without BSON header.';
      }
      var reader = new FileReader();
      reader.onload  = function() {
        var uint8Array = new Uint8Array(this.result);
        var msg = BSON.deserialize(uint8Array);
        callback(msg);
      };
      reader.readAsArrayBuffer(data);
    }
  
    return {
      /**
       * Emits a 'connection' event on WebSocket connection.
       *
       * @param event - the argument to emit with the event.
       * @memberof SocketAdapter
       */
      onopen: function onOpen(event) {
        client.isConnected = true;
        client.emit('connection', event);
      },
  
      /**
       * Emits a 'close' event on WebSocket disconnection.
       *
       * @param event - the argument to emit with the event.
       * @memberof SocketAdapter
       */
      onclose: function onClose(event) {
        client.isConnected = false;
        client.emit('close', event);
      },
  
      /**
       * Emits an 'error' event whenever there was an error.
       *
       * @param event - the argument to emit with the event.
       * @memberof SocketAdapter
       */
      onerror: function onError(event) {
        client.emit('error', event);
      },
  
      /**
       * Parses message responses from rosbridge and sends to the appropriate
       * topic, service, or param.
       *
       * @param message - the raw JSON message from rosbridge.
       * @memberof SocketAdapter
       */
      onmessage: function onMessage(data) {
        if (typeof Blob !== 'undefined' && data.data instanceof Blob) {
          decodeBSON(data.data, function (message) {
            handlePng(message, handleMessage);
          });
        } else if (data.data instanceof ArrayBuffer) {
          var decoded = CBOR.decode(data.data, typedArrayTagger);
          handleMessage(decoded);
        } else {
          var message = JSON.parse(typeof data === 'string' ? data : data.data);
          handlePng(message, handleMessage);
        }
      }
    };
  }
  
  module.exports = SocketAdapter;
  
  },{"../util/cborTypedArrayTags":41,"../util/decompressPng":44,"cbor-js":1,"ws":42}],19:[function(require,module,exports){
  /**
   * @fileoverview
   * @author Brandon Alexander - baalexander@gmail.com
   */
  
  var EventEmitter2 = require('eventemitter2').EventEmitter2;
  var Message = require('./Message');
  
  /**
   * Publish and/or subscribe to a topic in ROS.
   *
   * Emits the following events:
   *  * 'warning' - if there are any warning during the Topic creation
   *  * 'message' - the message data from rosbridge
   *
   * @constructor
   * @param options - object with following keys:
   *   * ros - the ROSLIB.Ros connection handle
   *   * name - the topic name, like /cmd_vel
   *   * messageType - the message type, like 'std_msgs/String'
   *   * compression - the type of compression to use, like 'png' or 'cbor'
   *   * throttle_rate - the rate (in ms in between messages) at which to throttle the topics
   *   * queue_size - the queue created at bridge side for re-publishing webtopics (defaults to 100)
   *   * latch - latch the topic when publishing
   *   * queue_length - the queue length at bridge side used when subscribing (defaults to 0, no queueing).
   *   * reconnect_on_close - the flag to enable resubscription and readvertisement on close event(defaults to true).
   */
  function Topic(options) {
    options = options || {};
    this.ros = options.ros;
    this.name = options.name;
    this.messageType = options.messageType;
    this.isAdvertised = false;
    this.compression = options.compression || 'none';
    this.throttle_rate = options.throttle_rate || 0;
    this.latch = options.latch || false;
    this.queue_size = options.queue_size || 100;
    this.queue_length = options.queue_length || 0;
    this.reconnect_on_close = options.reconnect_on_close || true;
  
    // Check for valid compression types
    if (this.compression && this.compression !== 'png' &&
      this.compression !== 'cbor' && this.compression !== 'none') {
      this.emit('warning', this.compression +
        ' compression is not supported. No compression will be used.');
      this.compression = 'none';
    }
  
    // Check if throttle rate is negative
    if (this.throttle_rate < 0) {
      this.emit('warning', this.throttle_rate + ' is not allowed. Set to 0');
      this.throttle_rate = 0;
    }
  
    var that = this;
    if (this.reconnect_on_close) {
      this.callForSubscribeAndAdvertise = function(message) {
        that.ros.callOnConnection(message);
  
        that.waitForReconnect = false;
        that.reconnectFunc = function() {
          if(!that.waitForReconnect) {
            that.waitForReconnect = true;
            that.ros.callOnConnection(message);
            that.ros.once('connection', function() {
              that.waitForReconnect = false;
            });
          }
        };
        that.ros.on('close', that.reconnectFunc);
      };
    }
    else {
      this.callForSubscribeAndAdvertise = this.ros.callOnConnection;
    }
  
    this._messageCallback = function(data) {
      that.emit('message', new Message(data));
    };
  }
  Topic.prototype.__proto__ = EventEmitter2.prototype;
  
  /**
   * Every time a message is published for the given topic, the callback
   * will be called with the message object.
   *
   * @param callback - function with the following params:
   *   * message - the published message
   */
  Topic.prototype.subscribe = function(callback) {
    if (typeof callback === 'function') {
      this.on('message', callback);
    }
  
    if (this.subscribeId) { return; }
    this.ros.on(this.name, this._messageCallback);
    this.subscribeId = 'subscribe:' + this.name + ':' + (++this.ros.idCounter);
  
    this.callForSubscribeAndAdvertise({
      op: 'subscribe',
      id: this.subscribeId,
      type: this.messageType,
      topic: this.name,
      compression: this.compression,
      throttle_rate: this.throttle_rate,
      queue_length: this.queue_length
    });
  };
  
  /**
   * Unregisters as a subscriber for the topic. Unsubscribing stop remove
   * all subscribe callbacks. To remove a call back, you must explicitly
   * pass the callback function in.
   *
   * @param callback - the optional callback to unregister, if
   *     * provided and other listeners are registered the topic won't
   *     * unsubscribe, just stop emitting to the passed listener
   */
  Topic.prototype.unsubscribe = function(callback) {
    if (callback) {
      this.off('message', callback);
      // If there is any other callbacks still subscribed don't unsubscribe
      if (this.listeners('message').length) { return; }
    }
    if (!this.subscribeId) { return; }
    // Note: Don't call this.removeAllListeners, allow client to handle that themselves
    this.ros.off(this.name, this._messageCallback);
    if(this.reconnect_on_close) {
      this.ros.off('close', this.reconnectFunc);
    }
    this.emit('unsubscribe');
    this.ros.callOnConnection({
      op: 'unsubscribe',
      id: this.subscribeId,
      topic: this.name
    });
    this.subscribeId = null;
  };
  
  
  /**
   * Registers as a publisher for the topic.
   */
  Topic.prototype.advertise = function() {
    if (this.isAdvertised) {
      return;
    }
    this.advertiseId = 'advertise:' + this.name + ':' + (++this.ros.idCounter);
    this.callForSubscribeAndAdvertise({
      op: 'advertise',
      id: this.advertiseId,
      type: this.messageType,
      topic: this.name,
      latch: this.latch,
      queue_size: this.queue_size
    });
    this.isAdvertised = true;
  
    if(!this.reconnect_on_close) {
      var that = this;
      this.ros.on('close', function() {
        that.isAdvertised = false;
      });
    }
  };
  
  /**
   * Unregisters as a publisher for the topic.
   */
  Topic.prototype.unadvertise = function() {
    if (!this.isAdvertised) {
      return;
    }
    if(this.reconnect_on_close) {
      this.ros.off('close', this.reconnectFunc);
    }
    this.emit('unadvertise');
    this.ros.callOnConnection({
      op: 'unadvertise',
      id: this.advertiseId,
      topic: this.name
    });
    this.isAdvertised = false;
  };
  
  /**
   * Publish the message.
   *
   * @param message - A ROSLIB.Message object.
   */
  Topic.prototype.publish = function(message) {
    if (!this.isAdvertised) {
      this.advertise();
    }
  
    this.ros.idCounter++;
    var call = {
      op: 'publish',
      id: 'publish:' + this.name + ':' + this.ros.idCounter,
      topic: this.name,
      msg: message,
      latch: this.latch
    };
    this.ros.callOnConnection(call);
  };
  
  module.exports = Topic;
  
  },{"./Message":12,"eventemitter2":2}],20:[function(require,module,exports){
  var mixin = require('../mixin');
  
  var core = module.exports = {
      Ros: require('./Ros'),
      Topic: require('./Topic'),
      Message: require('./Message'),
      Param: require('./Param'),
      Service: require('./Service'),
      ServiceRequest: require('./ServiceRequest'),
      ServiceResponse: require('./ServiceResponse')
  };
  
  mixin(core.Ros, ['Param', 'Service', 'Topic'], core);
  
  },{"../mixin":26,"./Message":12,"./Param":13,"./Ros":14,"./Service":15,"./ServiceRequest":16,"./ServiceResponse":17,"./Topic":19}],21:[function(require,module,exports){
  /**
   * @fileoverview
   * @author David Gossow - dgossow@willowgarage.com
   */
  
  var Vector3 = require('./Vector3');
  var Quaternion = require('./Quaternion');
  
  /**
   * A Pose in 3D space. Values are copied into this object.
   *
   *  @constructor
   *  @param options - object with following keys:
   *   * position - the Vector3 describing the position
   *   * orientation - the ROSLIB.Quaternion describing the orientation
   */
  function Pose(options) {
    options = options || {};
    // copy the values into this object if they exist
    this.position = new Vector3(options.position);
    this.orientation = new Quaternion(options.orientation);
  }
  
  /**
   * Apply a transform against this pose.
   *
   * @param tf the transform
   */
  Pose.prototype.applyTransform = function(tf) {
    this.position.multiplyQuaternion(tf.rotation);
    this.position.add(tf.translation);
    var tmp = tf.rotation.clone();
    tmp.multiply(this.orientation);
    this.orientation = tmp;
  };
  
  /**
   * Clone a copy of this pose.
   *
   * @returns the cloned pose
   */
  Pose.prototype.clone = function() {
    return new Pose(this);
  };
  
  /**
   * Multiplies this pose with another pose without altering this pose.
   *
   * @returns Result of multiplication.
   */
  Pose.prototype.multiply = function(pose) {
    var p = pose.clone();
    p.applyTransform({ rotation: this.orientation, translation: this.position });
    return p;
  };
  
  /**
   * Computes the inverse of this pose.
   *
   * @returns Inverse of pose.
   */
  Pose.prototype.getInverse = function() {
    var inverse = this.clone();
    inverse.orientation.invert();
    inverse.position.multiplyQuaternion(inverse.orientation);
    inverse.position.x *= -1;
    inverse.position.y *= -1;
    inverse.position.z *= -1;
    return inverse;
  };
  
  module.exports = Pose;
  },{"./Quaternion":22,"./Vector3":24}],22:[function(require,module,exports){
  /**
   * @fileoverview
   * @author David Gossow - dgossow@willowgarage.com
   */
  
  /**
   * A Quaternion.
   *
   *  @constructor
   *  @param options - object with following keys:
   *   * x - the x value
   *   * y - the y value
   *   * z - the z value
   *   * w - the w value
   */
  function Quaternion(options) {
    options = options || {};
    this.x = options.x || 0;
    this.y = options.y || 0;
    this.z = options.z || 0;
    this.w = (typeof options.w === 'number') ? options.w : 1;
  }
  
  /**
   * Perform a conjugation on this quaternion.
   */
  Quaternion.prototype.conjugate = function() {
    this.x *= -1;
    this.y *= -1;
    this.z *= -1;
  };
  
  /**
   * Return the norm of this quaternion.
   */
  Quaternion.prototype.norm = function() {
    return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
  };
  
  /**
   * Perform a normalization on this quaternion.
   */
  Quaternion.prototype.normalize = function() {
    var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
    if (l === 0) {
      this.x = 0;
      this.y = 0;
      this.z = 0;
      this.w = 1;
    } else {
      l = 1 / l;
      this.x = this.x * l;
      this.y = this.y * l;
      this.z = this.z * l;
      this.w = this.w * l;
    }
  };
  
  /**
   * Convert this quaternion into its inverse.
   */
  Quaternion.prototype.invert = function() {
    this.conjugate();
    this.normalize();
  };
  
  /**
   * Set the values of this quaternion to the product of itself and the given quaternion.
   *
   * @param q the quaternion to multiply with
   */
  Quaternion.prototype.multiply = function(q) {
    var newX = this.x * q.w + this.y * q.z - this.z * q.y + this.w * q.x;
    var newY = -this.x * q.z + this.y * q.w + this.z * q.x + this.w * q.y;
    var newZ = this.x * q.y - this.y * q.x + this.z * q.w + this.w * q.z;
    var newW = -this.x * q.x - this.y * q.y - this.z * q.z + this.w * q.w;
    this.x = newX;
    this.y = newY;
    this.z = newZ;
    this.w = newW;
  };
  
  /**
   * Clone a copy of this quaternion.
   *
   * @returns the cloned quaternion
   */
  Quaternion.prototype.clone = function() {
    return new Quaternion(this);
  };
  
  module.exports = Quaternion;
  
  },{}],23:[function(require,module,exports){
  /**
   * @fileoverview
   * @author David Gossow - dgossow@willowgarage.com
   */
  
  var Vector3 = require('./Vector3');
  var Quaternion = require('./Quaternion');
  
  /**
   * A Transform in 3-space. Values are copied into this object.
   *
   *  @constructor
   *  @param options - object with following keys:
   *   * translation - the Vector3 describing the translation
   *   * rotation - the ROSLIB.Quaternion describing the rotation
   */
  function Transform(options) {
    options = options || {};
    // Copy the values into this object if they exist
    this.translation = new Vector3(options.translation);
    this.rotation = new Quaternion(options.rotation);
  }
  
  /**
   * Clone a copy of this transform.
   *
   * @returns the cloned transform
   */
  Transform.prototype.clone = function() {
    return new Transform(this);
  };
  
  module.exports = Transform;
  },{"./Quaternion":22,"./Vector3":24}],24:[function(require,module,exports){
  /**
   * @fileoverview
   * @author David Gossow - dgossow@willowgarage.com
   */
  
  /**
   * A 3D vector.
   *
   *  @constructor
   *  @param options - object with following keys:
   *   * x - the x value
   *   * y - the y value
   *   * z - the z value
   */
  function Vector3(options) {
    options = options || {};
    this.x = options.x || 0;
    this.y = options.y || 0;
    this.z = options.z || 0;
  }
  
  /**
   * Set the values of this vector to the sum of itself and the given vector.
   *
   * @param v the vector to add with
   */
  Vector3.prototype.add = function(v) {
    this.x += v.x;
    this.y += v.y;
    this.z += v.z;
  };
  
  /**
   * Set the values of this vector to the difference of itself and the given vector.
   *
   * @param v the vector to subtract with
   */
  Vector3.prototype.subtract = function(v) {
    this.x -= v.x;
    this.y -= v.y;
    this.z -= v.z;
  };
  
  /**
   * Multiply the given Quaternion with this vector.
   *
   * @param q - the quaternion to multiply with
   */
  Vector3.prototype.multiplyQuaternion = function(q) {
    var ix = q.w * this.x + q.y * this.z - q.z * this.y;
    var iy = q.w * this.y + q.z * this.x - q.x * this.z;
    var iz = q.w * this.z + q.x * this.y - q.y * this.x;
    var iw = -q.x * this.x - q.y * this.y - q.z * this.z;
    this.x = ix * q.w + iw * -q.x + iy * -q.z - iz * -q.y;
    this.y = iy * q.w + iw * -q.y + iz * -q.x - ix * -q.z;
    this.z = iz * q.w + iw * -q.z + ix * -q.y - iy * -q.x;
  };
  
  /**
   * Clone a copy of this vector.
   *
   * @returns the cloned vector
   */
  Vector3.prototype.clone = function() {
    return new Vector3(this);
  };
  
  module.exports = Vector3;
  },{}],25:[function(require,module,exports){
  module.exports = {
      Pose: require('./Pose'),
      Quaternion: require('./Quaternion'),
      Transform: require('./Transform'),
      Vector3: require('./Vector3')
  };
  
  },{"./Pose":21,"./Quaternion":22,"./Transform":23,"./Vector3":24}],26:[function(require,module,exports){
  /**
   * Mixin a feature to the core/Ros prototype.
   * For example, mixin(Ros, ['Topic'], {Topic: <Topic>})
   * will add a topic bound to any Ros instances so a user
   * can call `var topic = ros.Topic({name: '/foo'});`
   *
   * @author Graeme Yeates - github.com/megawac
   */
  module.exports = function(Ros, classes, features) {
      classes.forEach(function(className) {
          var Class = features[className];
          Ros.prototype[className] = function(options) {
              options.ros = this;
              return new Class(options);
          };
      });
  };
  
  },{}],27:[function(require,module,exports){
  /**
   * @fileoverview
   * @author David Gossow - dgossow@willowgarage.com
   */
  
  var ActionClient = require('../actionlib/ActionClient');
  var Goal = require('../actionlib/Goal');
  
  var Service = require('../core/Service.js');
  var ServiceRequest = require('../core/ServiceRequest.js');
  
  var Transform = require('../math/Transform');
  
  /**
   * A TF Client that listens to TFs from tf2_web_republisher.
   *
   *  @constructor
   *  @param options - object with following keys:
   *   * ros - the ROSLIB.Ros connection handle
   *   * fixedFrame - the fixed frame, like /base_link
   *   * angularThres - the angular threshold for the TF republisher
   *   * transThres - the translation threshold for the TF republisher
   *   * rate - the rate for the TF republisher
   *   * updateDelay - the time (in ms) to wait after a new subscription
   *                   to update the TF republisher's list of TFs
   *   * topicTimeout - the timeout parameter for the TF republisher
   *   * serverName (optional) - the name of the tf2_web_republisher server
   *   * repubServiceName (optional) - the name of the republish_tfs service (non groovy compatibility mode only)
   *   																 default: '/republish_tfs'
   */
  function TFClient(options) {
    options = options || {};
    this.ros = options.ros;
    this.fixedFrame = options.fixedFrame || '/base_link';
    this.angularThres = options.angularThres || 2.0;
    this.transThres = options.transThres || 0.01;
    this.rate = options.rate || 10.0;
    this.updateDelay = options.updateDelay || 50;
    var seconds = options.topicTimeout || 2.0;
    var secs = Math.floor(seconds);
    var nsecs = Math.floor((seconds - secs) * 1000000000);
    this.topicTimeout = {
      secs: secs,
      nsecs: nsecs
    };
    this.serverName = options.serverName || '/tf2_web_republisher';
    this.repubServiceName = options.repubServiceName || '/republish_tfs';
  
    this.currentGoal = false;
    this.currentTopic = false;
    this.frameInfos = {};
    this.republisherUpdateRequested = false;
  
    // Create an Action client
    this.actionClient = this.ros.ActionClient({
      serverName : this.serverName,
      actionName : 'tf2_web_republisher/TFSubscriptionAction',
      omitStatus : true,
      omitResult : true
    });
  
    // Create a Service client
    this.serviceClient = this.ros.Service({
      name: this.repubServiceName,
      serviceType: 'tf2_web_republisher/RepublishTFs'
    });
  }
  
  /**
   * Process the incoming TF message and send them out using the callback
   * functions.
   *
   * @param tf - the TF message from the server
   */
  TFClient.prototype.processTFArray = function(tf) {
    var that = this;
    tf.transforms.forEach(function(transform) {
      var frameID = transform.child_frame_id;
      if (frameID[0] === '/')
      {
        frameID = frameID.substring(1);
      }
      var info = this.frameInfos[frameID];
      if (info) {
        info.transform = new Transform({
          translation : transform.transform.translation,
          rotation : transform.transform.rotation
        });
        info.cbs.forEach(function(cb) {
          cb(info.transform);
        });
      }
    }, this);
  };
  
  /**
   * Create and send a new goal (or service request) to the tf2_web_republisher
   * based on the current list of TFs.
   */
  TFClient.prototype.updateGoal = function() {
    var goalMessage = {
      source_frames : Object.keys(this.frameInfos),
      target_frame : this.fixedFrame,
      angular_thres : this.angularThres,
      trans_thres : this.transThres,
      rate : this.rate
    };
  
    // if we're running in groovy compatibility mode (the default)
    // then use the action interface to tf2_web_republisher
    if(this.ros.groovyCompatibility) {
      if (this.currentGoal) {
        this.currentGoal.cancel();
      }
      this.currentGoal = new Goal({
        actionClient : this.actionClient,
        goalMessage : goalMessage
      });
  
      this.currentGoal.on('feedback', this.processTFArray.bind(this));
      this.currentGoal.send();
    }
    else {
      // otherwise, use the service interface
      // The service interface has the same parameters as the action,
      // plus the timeout
      goalMessage.timeout = this.topicTimeout;
      var request = new ServiceRequest(goalMessage);
  
      this.serviceClient.callService(request, this.processResponse.bind(this));
    }
  
    this.republisherUpdateRequested = false;
  };
  
  /**
   * Process the service response and subscribe to the tf republisher
   * topic
   *
   * @param response the service response containing the topic name
   */
  TFClient.prototype.processResponse = function(response) {
    // if we subscribed to a topic before, unsubscribe so
    // the republisher stops publishing it
    if (this.currentTopic) {
      this.currentTopic.unsubscribe();
    }
  
    this.currentTopic = this.ros.Topic({
      name: response.topic_name,
      messageType: 'tf2_web_republisher/TFArray'
    });
    this.currentTopic.subscribe(this.processTFArray.bind(this));
  };
  
  /**
   * Subscribe to the given TF frame.
   *
   * @param frameID - the TF frame to subscribe to
   * @param callback - function with params:
   *   * transform - the transform data
   */
  TFClient.prototype.subscribe = function(frameID, callback) {
    // remove leading slash, if it's there
    if (frameID[0] === '/')
    {
      frameID = frameID.substring(1);
    }
    // if there is no callback registered for the given frame, create emtpy callback list
    if (!this.frameInfos[frameID]) {
      this.frameInfos[frameID] = {
        cbs: []
      };
      if (!this.republisherUpdateRequested) {
        setTimeout(this.updateGoal.bind(this), this.updateDelay);
        this.republisherUpdateRequested = true;
      }
    }
    // if we already have a transform, call back immediately
    else if (this.frameInfos[frameID].transform) {
      callback(this.frameInfos[frameID].transform);
    }
    this.frameInfos[frameID].cbs.push(callback);
  };
  
  /**
   * Unsubscribe from the given TF frame.
   *
   * @param frameID - the TF frame to unsubscribe from
   * @param callback - the callback function to remove
   */
  TFClient.prototype.unsubscribe = function(frameID, callback) {
    // remove leading slash, if it's there
    if (frameID[0] === '/')
    {
      frameID = frameID.substring(1);
    }
    var info = this.frameInfos[frameID];
    for (var cbs = info && info.cbs || [], idx = cbs.length; idx--;) {
      if (cbs[idx] === callback) {
        cbs.splice(idx, 1);
      }
    }
    if (!callback || cbs.length === 0) {
      delete this.frameInfos[frameID];
    }
  };
  
  /**
   * Unsubscribe and unadvertise all topics associated with this TFClient.
   */
  TFClient.prototype.dispose = function() {
    this.actionClient.dispose();
    if (this.currentTopic) {
      this.currentTopic.unsubscribe();
    }
  };
  
  module.exports = TFClient;
  
  },{"../actionlib/ActionClient":7,"../actionlib/Goal":9,"../core/Service.js":15,"../core/ServiceRequest.js":16,"../math/Transform":23}],28:[function(require,module,exports){
  var Ros = require('../core/Ros');
  var mixin = require('../mixin');
  
  var tf = module.exports = {
      TFClient: require('./TFClient')
  };
  
  mixin(Ros, ['TFClient'], tf);
  },{"../core/Ros":14,"../mixin":26,"./TFClient":27}],29:[function(require,module,exports){
  /**
   * @fileOverview 
   * @author Benjamin Pitzer - ben.pitzer@gmail.com
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var Vector3 = require('../math/Vector3');
  var UrdfTypes = require('./UrdfTypes');
  
  /**
   * A Box element in a URDF.
   *
   * @constructor
   * @param options - object with following keys:
   *  * xml - the XML element to parse
   */
  function UrdfBox(options) {
    this.dimension = null;
    this.type = UrdfTypes.URDF_BOX;
  
    // Parse the xml string
    var xyz = options.xml.getAttribute('size').split(' ');
    this.dimension = new Vector3({
      x : parseFloat(xyz[0]),
      y : parseFloat(xyz[1]),
      z : parseFloat(xyz[2])
    });
  }
  
  module.exports = UrdfBox;
  },{"../math/Vector3":24,"./UrdfTypes":38}],30:[function(require,module,exports){
  /**
   * @fileOverview 
   * @author Benjamin Pitzer - ben.pitzer@gmail.com
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  /**
   * A Color element in a URDF.
   *
   * @constructor
   * @param options - object with following keys:
   *  * xml - the XML element to parse
   */
  function UrdfColor(options) {
    // Parse the xml string
    var rgba = options.xml.getAttribute('rgba').split(' ');
    this.r = parseFloat(rgba[0]);
    this.g = parseFloat(rgba[1]);
    this.b = parseFloat(rgba[2]);
    this.a = parseFloat(rgba[3]);
  }
  
  module.exports = UrdfColor;
  },{}],31:[function(require,module,exports){
  /**
   * @fileOverview 
   * @author Benjamin Pitzer - ben.pitzer@gmail.com
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var UrdfTypes = require('./UrdfTypes');
  
  /**
   * A Cylinder element in a URDF.
   *
   * @constructor
   * @param options - object with following keys:
   *  * xml - the XML element to parse
   */
  function UrdfCylinder(options) {
    this.type = UrdfTypes.URDF_CYLINDER;
    this.length = parseFloat(options.xml.getAttribute('length'));
    this.radius = parseFloat(options.xml.getAttribute('radius'));
  }
  
  module.exports = UrdfCylinder;
  },{"./UrdfTypes":38}],32:[function(require,module,exports){
  /**
   * @fileOverview
   * @author David V. Lu!!  davidvlu@gmail.com
   */
  
  var Pose = require('../math/Pose');
  var Vector3 = require('../math/Vector3');
  var Quaternion = require('../math/Quaternion');
  
  /**
   * A Joint element in a URDF.
   *
   * @constructor
   * @param options - object with following keys:
   *  * xml - the XML element to parse
   */
  function UrdfJoint(options) {
    this.name = options.xml.getAttribute('name');
    this.type = options.xml.getAttribute('type');
  
    var parents = options.xml.getElementsByTagName('parent');
    if(parents.length > 0) {
      this.parent = parents[0].getAttribute('link');
    }
  
    var children = options.xml.getElementsByTagName('child');
    if(children.length > 0) {
      this.child = children[0].getAttribute('link');
    }
  
    var limits = options.xml.getElementsByTagName('limit');
    if (limits.length > 0) {
      this.minval = parseFloat( limits[0].getAttribute('lower') );
      this.maxval = parseFloat( limits[0].getAttribute('upper') );
    }
  
    // Origin
    var origins = options.xml.getElementsByTagName('origin');
    if (origins.length === 0) {
      // use the identity as the default
      this.origin = new Pose();
    } else {
      // Check the XYZ
      var xyz = origins[0].getAttribute('xyz');
      var position = new Vector3();
      if (xyz) {
        xyz = xyz.split(' ');
        position = new Vector3({
          x : parseFloat(xyz[0]),
          y : parseFloat(xyz[1]),
          z : parseFloat(xyz[2])
        });
      }
  
      // Check the RPY
      var rpy = origins[0].getAttribute('rpy');
      var orientation = new Quaternion();
      if (rpy) {
        rpy = rpy.split(' ');
        // Convert from RPY
        var roll = parseFloat(rpy[0]);
        var pitch = parseFloat(rpy[1]);
        var yaw = parseFloat(rpy[2]);
        var phi = roll / 2.0;
        var the = pitch / 2.0;
        var psi = yaw / 2.0;
        var x = Math.sin(phi) * Math.cos(the) * Math.cos(psi) - Math.cos(phi) * Math.sin(the)
            * Math.sin(psi);
        var y = Math.cos(phi) * Math.sin(the) * Math.cos(psi) + Math.sin(phi) * Math.cos(the)
            * Math.sin(psi);
        var z = Math.cos(phi) * Math.cos(the) * Math.sin(psi) - Math.sin(phi) * Math.sin(the)
            * Math.cos(psi);
        var w = Math.cos(phi) * Math.cos(the) * Math.cos(psi) + Math.sin(phi) * Math.sin(the)
            * Math.sin(psi);
  
        orientation = new Quaternion({
          x : x,
          y : y,
          z : z,
          w : w
        });
        orientation.normalize();
      }
      this.origin = new Pose({
        position : position,
        orientation : orientation
      });
    }
  }
  
  module.exports = UrdfJoint;
  
  },{"../math/Pose":21,"../math/Quaternion":22,"../math/Vector3":24}],33:[function(require,module,exports){
  /**
   * @fileOverview 
   * @author Benjamin Pitzer - ben.pitzer@gmail.com
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var UrdfVisual = require('./UrdfVisual');
  
  /**
   * A Link element in a URDF.
   *
   * @constructor
   * @param options - object with following keys:
   *  * xml - the XML element to parse
   */
  function UrdfLink(options) {
    this.name = options.xml.getAttribute('name');
    this.visuals = [];
    var visuals = options.xml.getElementsByTagName('visual');
  
    for( var i=0; i<visuals.length; i++ ) {
      this.visuals.push( new UrdfVisual({
        xml : visuals[i]
      }) );
    }
  }
  
  module.exports = UrdfLink;
  },{"./UrdfVisual":39}],34:[function(require,module,exports){
  /**
   * @fileOverview 
   * @author Benjamin Pitzer - ben.pitzer@gmail.com
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var UrdfColor = require('./UrdfColor');
  
  /**
   * A Material element in a URDF.
   *
   * @constructor
   * @param options - object with following keys:
   *  * xml - the XML element to parse
   */
  function UrdfMaterial(options) {
    this.textureFilename = null;
    this.color = null;
  
    this.name = options.xml.getAttribute('name');
  
    // Texture
    var textures = options.xml.getElementsByTagName('texture');
    if (textures.length > 0) {
      this.textureFilename = textures[0].getAttribute('filename');
    }
  
    // Color
    var colors = options.xml.getElementsByTagName('color');
    if (colors.length > 0) {
      // Parse the RBGA string
      this.color = new UrdfColor({
        xml : colors[0]
      });
    }
  }
  
  UrdfMaterial.prototype.isLink = function() {
    return this.color === null && this.textureFilename === null;
  };
  
  var assign = require('object-assign');
  
  UrdfMaterial.prototype.assign = function(obj) {
      return assign(this, obj);
  };
  
  module.exports = UrdfMaterial;
  
  },{"./UrdfColor":30,"object-assign":3}],35:[function(require,module,exports){
  /**
   * @fileOverview 
   * @author Benjamin Pitzer - ben.pitzer@gmail.com
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var Vector3 = require('../math/Vector3');
  var UrdfTypes = require('./UrdfTypes');
  
  /**
   * A Mesh element in a URDF.
   *
   * @constructor
   * @param options - object with following keys:
   *  * xml - the XML element to parse
   */
  function UrdfMesh(options) {
    this.scale = null;
  
    this.type = UrdfTypes.URDF_MESH;
    this.filename = options.xml.getAttribute('filename');
  
    // Check for a scale
    var scale = options.xml.getAttribute('scale');
    if (scale) {
      // Get the XYZ
      var xyz = scale.split(' ');
      this.scale = new Vector3({
        x : parseFloat(xyz[0]),
        y : parseFloat(xyz[1]),
        z : parseFloat(xyz[2])
      });
    }
  }
  
  module.exports = UrdfMesh;
  },{"../math/Vector3":24,"./UrdfTypes":38}],36:[function(require,module,exports){
  /**
   * @fileOverview 
   * @author Benjamin Pitzer - ben.pitzer@gmail.com
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var UrdfMaterial = require('./UrdfMaterial');
  var UrdfLink = require('./UrdfLink');
  var UrdfJoint = require('./UrdfJoint');
  var DOMParser = require('xmldom').DOMParser;
  
  // See https://developer.mozilla.org/docs/XPathResult#Constants
  var XPATH_FIRST_ORDERED_NODE_TYPE = 9;
  
  /**
   * A URDF Model can be used to parse a given URDF into the appropriate elements.
   *
   * @constructor
   * @param options - object with following keys:
   *  * xml - the XML element to parse
   *  * string - the XML element to parse as a string
   */
  function UrdfModel(options) {
    options = options || {};
    var xmlDoc = options.xml;
    var string = options.string;
    this.materials = {};
    this.links = {};
    this.joints = {};
  
    // Check if we are using a string or an XML element
    if (string) {
      // Parse the string
      var parser = new DOMParser();
      xmlDoc = parser.parseFromString(string, 'text/xml');
    }
  
    // Initialize the model with the given XML node.
    // Get the robot tag
    var robotXml = xmlDoc.documentElement;
  
    // Get the robot name
    this.name = robotXml.getAttribute('name');
  
    // Parse all the visual elements we need
    for (var nodes = robotXml.childNodes, i = 0; i < nodes.length; i++) {
      var node = nodes[i];
      if (node.tagName === 'material') {
        var material = new UrdfMaterial({
          xml : node
        });
        // Make sure this is unique
        if (this.materials[material.name] !== void 0) {
          if( this.materials[material.name].isLink() ) {
            this.materials[material.name].assign( material );
          } else {
            console.warn('Material ' + material.name + 'is not unique.');
          }
        } else {
          this.materials[material.name] = material;
        }
      } else if (node.tagName === 'link') {
        var link = new UrdfLink({
          xml : node
        });
        // Make sure this is unique
        if (this.links[link.name] !== void 0) {
          console.warn('Link ' + link.name + ' is not unique.');
        } else {
          // Check for a material
          for( var j=0; j<link.visuals.length; j++ )
          {
            var mat = link.visuals[j].material; 
            if ( mat !== null ) {
              if (this.materials[mat.name] !== void 0) {
                link.visuals[j].material = this.materials[mat.name];
              } else {
                this.materials[mat.name] = mat;
              }
            }
          }
  
          // Add the link
          this.links[link.name] = link;
        }
      } else if (node.tagName === 'joint') {
        var joint = new UrdfJoint({
          xml : node
        });
        this.joints[joint.name] = joint;
      }
    }
  }
  
  module.exports = UrdfModel;
  
  },{"./UrdfJoint":32,"./UrdfLink":33,"./UrdfMaterial":34,"xmldom":45}],37:[function(require,module,exports){
  /**
   * @fileOverview 
   * @author Benjamin Pitzer - ben.pitzer@gmail.com
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var UrdfTypes = require('./UrdfTypes');
  
  /**
   * A Sphere element in a URDF.
   *
   * @constructor
   * @param options - object with following keys:
   *  * xml - the XML element to parse
   */
  function UrdfSphere(options) {
    this.type = UrdfTypes.URDF_SPHERE;
    this.radius = parseFloat(options.xml.getAttribute('radius'));
  }
  
  module.exports = UrdfSphere;
  },{"./UrdfTypes":38}],38:[function(require,module,exports){
  module.exports = {
    URDF_SPHERE : 0,
    URDF_BOX : 1,
    URDF_CYLINDER : 2,
    URDF_MESH : 3
  };
  
  },{}],39:[function(require,module,exports){
  /**
   * @fileOverview 
   * @author Benjamin Pitzer - ben.pitzer@gmail.com
   * @author Russell Toris - rctoris@wpi.edu
   */
  
  var Pose = require('../math/Pose');
  var Vector3 = require('../math/Vector3');
  var Quaternion = require('../math/Quaternion');
  
  var UrdfCylinder = require('./UrdfCylinder');
  var UrdfBox = require('./UrdfBox');
  var UrdfMaterial = require('./UrdfMaterial');
  var UrdfMesh = require('./UrdfMesh');
  var UrdfSphere = require('./UrdfSphere');
  
  /**
   * A Visual element in a URDF.
   *
   * @constructor
   * @param options - object with following keys:
   *  * xml - the XML element to parse
   */
  function UrdfVisual(options) {
    var xml = options.xml;
    this.origin = null;
    this.geometry = null;
    this.material = null;
  
    // Origin
    var origins = xml.getElementsByTagName('origin');
    if (origins.length === 0) {
      // use the identity as the default
      this.origin = new Pose();
    } else {
      // Check the XYZ
      var xyz = origins[0].getAttribute('xyz');
      var position = new Vector3();
      if (xyz) {
        xyz = xyz.split(' ');
        position = new Vector3({
          x : parseFloat(xyz[0]),
          y : parseFloat(xyz[1]),
          z : parseFloat(xyz[2])
        });
      }
  
      // Check the RPY
      var rpy = origins[0].getAttribute('rpy');
      var orientation = new Quaternion();
      if (rpy) {
        rpy = rpy.split(' ');
        // Convert from RPY
        var roll = parseFloat(rpy[0]);
        var pitch = parseFloat(rpy[1]);
        var yaw = parseFloat(rpy[2]);
        var phi = roll / 2.0;
        var the = pitch / 2.0;
        var psi = yaw / 2.0;
        var x = Math.sin(phi) * Math.cos(the) * Math.cos(psi) - Math.cos(phi) * Math.sin(the)
            * Math.sin(psi);
        var y = Math.cos(phi) * Math.sin(the) * Math.cos(psi) + Math.sin(phi) * Math.cos(the)
            * Math.sin(psi);
        var z = Math.cos(phi) * Math.cos(the) * Math.sin(psi) - Math.sin(phi) * Math.sin(the)
            * Math.cos(psi);
        var w = Math.cos(phi) * Math.cos(the) * Math.cos(psi) + Math.sin(phi) * Math.sin(the)
            * Math.sin(psi);
  
        orientation = new Quaternion({
          x : x,
          y : y,
          z : z,
          w : w
        });
        orientation.normalize();
      }
      this.origin = new Pose({
        position : position,
        orientation : orientation
      });
    }
  
    // Geometry
    var geoms = xml.getElementsByTagName('geometry');
    if (geoms.length > 0) {
      var geom = geoms[0];
      var shape = null;
      // Check for the shape
      for (var i = 0; i < geom.childNodes.length; i++) {
        var node = geom.childNodes[i];
        if (node.nodeType === 1) {
          shape = node;
          break;
        }
      }
      // Check the type
      var type = shape.nodeName;
      if (type === 'sphere') {
        this.geometry = new UrdfSphere({
          xml : shape
        });
      } else if (type === 'box') {
        this.geometry = new UrdfBox({
          xml : shape
        });
      } else if (type === 'cylinder') {
        this.geometry = new UrdfCylinder({
          xml : shape
        });
      } else if (type === 'mesh') {
        this.geometry = new UrdfMesh({
          xml : shape
        });
      } else {
        console.warn('Unknown geometry type ' + type);
      }
    }
  
    // Material
    var materials = xml.getElementsByTagName('material');
    if (materials.length > 0) {
      this.material = new UrdfMaterial({
        xml : materials[0]
      });
    }
  }
  
  module.exports = UrdfVisual;
  },{"../math/Pose":21,"../math/Quaternion":22,"../math/Vector3":24,"./UrdfBox":29,"./UrdfCylinder":31,"./UrdfMaterial":34,"./UrdfMesh":35,"./UrdfSphere":37}],40:[function(require,module,exports){
  module.exports = require('object-assign')({
      UrdfBox: require('./UrdfBox'),
      UrdfColor: require('./UrdfColor'),
      UrdfCylinder: require('./UrdfCylinder'),
      UrdfLink: require('./UrdfLink'),
      UrdfMaterial: require('./UrdfMaterial'),
      UrdfMesh: require('./UrdfMesh'),
      UrdfModel: require('./UrdfModel'),
      UrdfSphere: require('./UrdfSphere'),
      UrdfVisual: require('./UrdfVisual')
  }, require('./UrdfTypes'));
  
  },{"./UrdfBox":29,"./UrdfColor":30,"./UrdfCylinder":31,"./UrdfLink":33,"./UrdfMaterial":34,"./UrdfMesh":35,"./UrdfModel":36,"./UrdfSphere":37,"./UrdfTypes":38,"./UrdfVisual":39,"object-assign":3}],41:[function(require,module,exports){
  'use strict';
  
  var UPPER32 = Math.pow(2, 32);
  
  var warnedPrecision = false;
  function warnPrecision() {
    if (!warnedPrecision) {
      warnedPrecision = true;
      console.warn('CBOR 64-bit integer array values may lose precision. No further warnings.');
    }
  }
  
  /**
   * Unpacks 64-bit unsigned integer from byte array.
   * @param {Uint8Array} bytes
  */
  function decodeUint64LE(bytes) {
    warnPrecision();
  
    var byteLen = bytes.byteLength;
    var offset = bytes.byteOffset;
    var arrLen = byteLen / 8;
  
    var buffer = bytes.buffer.slice(offset, offset + byteLen);
    var uint32View = new Uint32Array(buffer);
  
    var arr = new Array(arrLen);
    for (var i = 0; i < arrLen; i++) {
      var si = i * 2;
      var lo = uint32View[si];
      var hi = uint32View[si+1];
      arr[i] = lo + UPPER32 * hi;
    }
  
    return arr;
  }
  
  /**
   * Unpacks 64-bit signed integer from byte array.
   * @param {Uint8Array} bytes
  */
  function decodeInt64LE(bytes) {
    warnPrecision();
  
    var byteLen = bytes.byteLength;
    var offset = bytes.byteOffset;
    var arrLen = byteLen / 8;
  
    var buffer = bytes.buffer.slice(offset, offset + byteLen);
    var uint32View = new Uint32Array(buffer);
    var int32View = new Int32Array(buffer);
  
    var arr = new Array(arrLen);
    for (var i = 0; i < arrLen; i++) {
      var si = i * 2;
      var lo = uint32View[si];
      var hi = int32View[si+1];
      arr[i] = lo + UPPER32 * hi;
    }
  
    return arr;
  }
  
  /**
   * Unpacks typed array from byte array.
   * @param {Uint8Array} bytes
   * @param {type} ArrayType - desired output array type
  */
  function decodeNativeArray(bytes, ArrayType) {
    var byteLen = bytes.byteLength;
    var offset = bytes.byteOffset;
    var buffer = bytes.buffer.slice(offset, offset + byteLen);
    return new ArrayType(buffer);
  }
  
  /**
   * Support a subset of draft CBOR typed array tags:
   *   <https://tools.ietf.org/html/draft-ietf-cbor-array-tags-00>
   * Only support little-endian tags for now.
   */
  var nativeArrayTypes = {
    64: Uint8Array,
    69: Uint16Array,
    70: Uint32Array,
    72: Int8Array,
    77: Int16Array,
    78: Int32Array,
    85: Float32Array,
    86: Float64Array
  };
  
  /**
   * We can also decode 64-bit integer arrays, since ROS has these types.
   */
  var conversionArrayTypes = {
    71: decodeUint64LE,
    79: decodeInt64LE
  };
  
  /**
   * Handles CBOR typed array tags during decoding.
   * @param {Uint8Array} data
   * @param {Number} tag
   */
  function cborTypedArrayTagger(data, tag) {
    if (tag in nativeArrayTypes) {
      var arrayType = nativeArrayTypes[tag];
      return decodeNativeArray(data, arrayType);
    }
    if (tag in conversionArrayTypes) {
      return conversionArrayTypes[tag](data);
    }
    return data;
  }
  
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = cborTypedArrayTagger;
  }
  
  },{}],42:[function(require,module,exports){
  module.exports = window.WebSocket;
  
  },{}],43:[function(require,module,exports){
  /* global document */
  module.exports = function Canvas() {
    return document.createElement('canvas');
  };
  },{}],44:[function(require,module,exports){
  /**
   * @fileOverview
   * @author Graeme Yeates - github.com/megawac
   */
  
  'use strict';
  
  var Canvas = require('canvas');
  var Image = Canvas.Image || window.Image;
  
  /**
   * If a message was compressed as a PNG image (a compression hack since
   * gzipping over WebSockets * is not supported yet), this function places the
   * "image" in a canvas element then decodes the * "image" as a Base64 string.
   *
   * @private
   * @param data - object containing the PNG data.
   * @param callback - function with params:
   *   * data - the uncompressed data
   */
  function decompressPng(data, callback) {
    // Uncompresses the data before sending it through (use image/canvas to do so).
    var image = new Image();
    // When the image loads, extracts the raw data (JSON message).
    image.onload = function() {
      // Creates a local canvas to draw on.
      var canvas = new Canvas();
      var context = canvas.getContext('2d');
  
      // Sets width and height.
      canvas.width = image.width;
      canvas.height = image.height;
  
      // Prevents anti-aliasing and loosing data
      context.imageSmoothingEnabled = false;
      context.webkitImageSmoothingEnabled = false;
      context.mozImageSmoothingEnabled = false;
  
      // Puts the data into the image.
      context.drawImage(image, 0, 0);
      // Grabs the raw, uncompressed data.
      var imageData = context.getImageData(0, 0, image.width, image.height).data;
  
      // Constructs the JSON.
      var jsonData = '';
      for (var i = 0; i < imageData.length; i += 4) {
        // RGB
        jsonData += String.fromCharCode(imageData[i], imageData[i + 1], imageData[i + 2]);
      }
      callback(JSON.parse(jsonData));
    };
    // Sends the image data to load.
    image.src = 'data:image/png;base64,' + data;
  }
  
  module.exports = decompressPng;
  
  },{"canvas":43}],45:[function(require,module,exports){
  exports.DOMImplementation = window.DOMImplementation;
  exports.XMLSerializer = window.XMLSerializer;
  exports.DOMParser = window.DOMParser;
  
  },{}]},{},[6]);
  